2、感知机

感知机(perceptron)是二分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别,取+1和-1二值。感知机旨在求出将训练数据进行线性划分的分离超平面,因此,导入基于误分类的损失函数,利用梯度下降法对损失函数进行极小化,求得感知机模型。感知机的基本原理就是逐点修正,首先在超平面上随意取一条分类面,统计分类错误的点;然后随机对某个错误点就行修正,即变换直线的位置,使该错误点得以修正;接着再随机选择一个错误点进行纠正,分类面不断变化,直到所有的点都完全分类正确了,就得到了最佳的分类面。

1、感知机学习策略

为了找到这样的超平面,即确定感知机模型参数w,b,需要定义(经验)损失函数并将损失函数极小化。

首先在输入空间中任一点x0到超平面S的距离为:

对于模型而言,在分类错误的情况下,若,则实际的yi应该等于-1,而当时,yi等于1,因此由这个特性我们可以去掉上面的绝对值符号,将公式转化为

因此,误分类点xi到超平面S的距离为

由此得到所有误分类点到超平面S的总距离为

基于此,当给定训练数据集时,感知机学习的损失函数定义为

优化策略选择随机梯度下降法。

示例代码

import numpy as np
import matplotlib.pyplot as plt
import  random as rd


# generate train data
class GenerateData:
    def __init__(self, num_data, w_t, b_t, ratio):
        self.numData = num_data
        self.w_t = w_t
        self.b_t = b_t
    
    def generateData(self):
        # generate pos and neg samples
        x_t = np.array(range(self.numData))
        y_t = x_t * self.w_t + self.b_t 
        
        rd.seed(10)
        y_pos = []
        y_neg = []
        for y_i in y_t:
            limit = rd.randint(10, max(y_t))
            rand_pos = rd.randint(1, limit)
            y_pos.append(y_i + rand_pos)
            
            rand_neg = rd.randint(1, limit)
            y_neg.append(y_i - rand_neg)
        
        x = list(x_t) * 2
        y = y_pos + y_neg
        label = [1] * len(y_pos) + [-1] * len(y_neg)
        data = []
        for i in range(len(x)):
            data.append([x[i], y[i]])
        
        # shuffle the  train data
        tempData = list(zip(data, label))
        rd.shuffle(tempData)
        data, label = zip(*tempData)
        data = np.array(list(data))
        label = np.array(list(label))
        
        train_data, train_label, test_data, test_label = self.splitData(data, label)
        
        return train_data, train_label, test_data, test_label, x_t, y_t, y_pos, y_neg
        
    def splitData(self, data, label):
        numData = len(data)
        # split train data and test data 8:2
        train_data = data[:round(numData*0.8), :]
        test_data = data[round(numData*0.8):, :]

        train_label = label[:round(numData*0.8)]
        test_label = label[round(numData*0.8):]
        
        return train_data, train_label, test_data, test_label



class PLA:
    def __init__(self, train_data = [], real_result = [], eta = 1):
        self.train_data = train_data
        self.real_result = real_result
        self.w = np.zeros([1, len(train_data.T)], int)
        self.b = 0
        self.eta = eta
        
    def sign(self, x):
        if x > 0:
            return 1
        else:
            return -1
        
    def model(self, x):
        y = np.dot(x, self.w.T) + self.b
        y_pred = self.sign(y)   # predicted output
        return y_pred, y
    
    # compute loss
    def loss(self, fx, y):
        return int(fx) * y
    
    # update weight and bias
    def update(self, x, y):
        self.w = self.w + self.eta * y * x
        self.b = self.b + self.eta * y

    def train(self, count):
        update_count = 0
        num_data = len(self.train_data)
        
        while count > 0:
            count = count -1
            
            if num_data < 0:
                print('Exception exit')
                break
            
            # random pick train samples
            index = rd.randint(0, num_data - 1)
            x = self.train_data[index]
            y = self.real_result[index]
            
            pred_y, linear_y = self.model(x)
            
            loss_v = self.loss(y, linear_y)
            if loss_v > 0:
                continue
            update_count = update_count + 1
            self.update(x, y)
            print('loss = {}'.format(loss_v))
        print("update count: ", update_count)

    def verify(self, verify_data, verify_result):
        size = len(verify_data)
        failed_count = 0
        if size <= 0:
            pass
        for i in range(size):
            x = verify_data[i]
            y = verify_result.T[i]
            if self.loss(y, self.model(x)[1]) > 0:
                continue
            failed_count = failed_count + 1
        success_rate = (1.0 - (float(failed_count)/size))*100
        print("Success Rate: ", success_rate, "%")
        print("All input: ", size, " failed_count: ", failed_count)

    def predict(self, predict_data):
        size = len(predict_data)
        result = []
        if size <= 0:
            pass
        for i in range(size):
            x = verify_data[i]
            y = verify_result.T[i]
            result.append(self.model(x)[0])
        return result

if __name__ == '__main__':
    
    num_data = 300    # number of samples
    w_t = 2
    b_t = 5
    ratio = 0.8  # train:test=4:1
    generater = GenerateData(num_data, w_t, b_t, ratio)
    train_data, train_label, test_data, test_label, x_t, y_t, y_pos, y_neg = generater.generateData()

    # train
    num_iter = 5000   # number of iterations
    perceptron = PLA(train_data, train_label)
    perceptron.train(num_iter)
    
    # test
    perceptron.verify(test_data, test_label)
    print("T1: w:", perceptron.w," b:", perceptron.b)
    
    # 1. draw the (x,y) points
    plt.figure(figsize = (10, 8))
    plt.scatter(x_t, y_pos, c='y')
    plt.scatter(x_t, y_neg, c='b')
    # 2. draw y=gradient*x+offset line
    plt.plot(x_t, y_t, color="g")
    # 3. draw the line w_1*x_1 + w_2*x_2 + b = 0
    plt.plot(x_t, -(np.array(x_t).dot(float(perceptron.w.T[0]))+float(perceptron.b))/float(perceptron.w.T[1]), 
                              color='r')
    plt.show()

得到的结果如下:

 

测试准确率为98.3%

代码参考自: https://blog.csdn.net/yxhlfx/article/details/79093456

2、感知机学习算法的对偶形式

对偶形式的基本想法是,将w和b表示为实例xi和标记yi的线性组合的形式,通过求解其系数而求得w和b,不是一般性,在算法2.1中可假设初始值w0,b0均为0,对误分类点(xi,yi)通过

逐步修改w,b,设修改n次,则w,b关于(xi,yi)的增量分别是,这里。因此,最后学习到的w,b可以分别表示为

经过如上变换后,每次训练迭代更新就需要更新α与b值。更新策略改变为如下:

同时观察模型可以发现xi与xj以内积的形式出现,通过这个热证可以想到Gram矩阵的定义

而在对偶形式当中,α与损失函数进行梯度下降的次数是紧密联系的,随着α的不断增加,损失函数执行梯度下降的次数不断增加,模型也越接近真实情况。

感知机的限制与推广

限制:感知机的限制主要是只能处理线性的情况,不能解决线性不可分的问题。

推广:可以应用于多维空间的多分类问题

示例:应用感知机解决MNIST分类问题

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值