天天看点

机器学习之感知机

1.概念

感知机是一种较为简单的二分类线性模型,其属于判别模型。

其定义为:

假设训练数据集是线性可分的,感知机学习的目标是求得一个能够将训练数据集正实例点和负实例点完全正确分开的分离超平面,也是就是+1/-1的线性分离超平面。

如果是非线性可分的数据,则最后无法获得超平面,因此后来引出来了两个方向:神经网络和支持向量机。

那么,什么叫做超平面呢?

定义:可以参考这篇博客的解释,点此处。

在二维空间中超平面是一条线,在三维空间中的超平面是一个平面。超平面可以将混杂的数据种类进行划分。

2.感知机模型原理

假设输入x为任意实例的特征向量,输出y={+1,-1}为实例的类别,则输入到输出满足以下映射函数关系:

机器学习之感知机

其中,w为权值列向量,b为偏置,而w·x+b=0就是构成的线性分离超平面。也不难理解,就是一个线性模型,sign()函数为符号函数,满足:

机器学习之感知机

几何解释:

wx+b对应于特征空间中的一个分离超平面S,其中w是S的法向量,b是S的截距.S将特征空间划分为两个部分,位于两个部分的点分别被分为正实例和负实例两类,但感知机的必要前提是数据必须线性可分的。

3.感知机损失函数

假设训练数据集是线性可分的,感知机的损失函数是误分类点到超平面S的总距离.因为误分类点到超平面S的距离是:(点到超平面的距离)

机器学习之感知机

且对于误分类的数据来说,总有

机器学习之感知机

成立,因此不考虑1/||w||,就得到感知机的损失函数:,

机器学习之感知机

其中M是误分类点的集合.感知机学习的策略就是选取使损失函数最小的模型参数.

问题:为什么不考虑1/||w||?原因可以列为以下两点。
  • 1/||w|| 不影响yi(w⋅xi+b) 正负的判断,即不影响学习算法的中间过程。 因为感知机学习算法是误分类驱动的,这里需要注意的是所谓的误分类驱动指的是我们只需要判断−yi(w⋅xi+b)

    的正负来判断分类的正确与否,而1/||w|| 并不影响正负值的判断。所以1/||w|| 对感知机学习算法的中间过程可以不考虑。

  • 1/||w|| 不影响感知机学习算法的最终结果。因为感知机学习算法最终的终止条件是所有的输入都被正确分类,即不存在误分类的点。则此时损失函数为0.

    对应于−1/||w||∑i∈Myi(w⋅xi+b) ,即分子为0.则可以看出1/||w||

    对最终结果也无影响。综上所述,即使忽略1/||w|| ,也不会对感知机学习算法的执行过程产生任何影响。反而还能简化运算,提高算法执行效率。

对于以上损失函数,可以采用梯度下降法更新学习到的参数,使得最小化损失函数:

机器学习之感知机

由上可知,感知机算法包括参数初始化、对每个数据点判断其是否误分,如果误分,则按照梯度下降法更新超平面参数,直至没有误分类点。

4.算法

感知机学习算法是对上述损失函数进行极小化,求得w和b。但是用普通的基于所有样本的梯度和的均值的批量梯度下降法(BGD)是行不通的,原因在于我们的损失函数里面有限定,只有误分类的M集合里面的样本才能参与损失函数的优化。所以我们不能用最普通的批量梯度下降,只能采用随机梯度下降(SGD)。目标函数如下:

机器学习之感知机

原始形式算法:

机器学习之感知机

对偶形式算法:

假设原始形式中的w0和b0均为0,设逐步修改w和b共n次,令a=nη,最后学习到的w,b可以表示为

机器学习之感知机

那么对偶算法就变为设初始a和b均为0,每次选取数据更新a和b直至没有误分类点为止.对偶形式的意义在于可以将训练集中实例间的内积计算出来,存在Gram矩阵中,可以大大加快训练速度.

机器学习之感知机

5.算法的选择

在向量维数(特征数)过高时,计算内积非常耗时,应选择对偶形式算法加速。

在向量个数(样本数)过多时,每次计算累计和就没有必要,应选择原始算法。

6.代码实现

原始形式算法&对偶形式算法:

# -*- coding: utf-8 -*-
import numpy as np
class Perceptron(object):
    def __init__(self, input_x, feature_num, input_y, learn_rate=1):
        self._input_x = np.array(input_x) # 输入数据集中的X
        self._input_y = np.array(input_y) # 输入数据集中的Y
        self._feature_num = feature_num   # 总共有多少个特征
        self._rate = learn_rate           # 学习速率
        self._final_w = 0                 # 最后学习到的w
        self._final_b = 0                 # 最后学习到的b
        
    def sgd_train(self): #算法原始形式
        total = len(self._input_y)
        feature_num = range(self._feature_num)
        data_num = range(total)
        w = np.zeros(self._feature_num)   #初始化向量w
        b = 0

        while True:
            separted = True
            for i in data_num:           # 遍历数据集,查找误分类点
                inner = np.inner(w, self._input_x[i]) //内积函数
                if self._input_y[i] * (inner+b) <= 0: # 误分类点
                    separted = False
                    w = w + self._rate * self._input_y[i] * self._input_x[i]
                    b = b + self._rate * self._input_y[i]
            if separted:
                break
            else:
                continue
        self._final_w = w
        self._final_b = b
        print(self._final_w, self._final_b)

    def pair_sgd_train(self): # 对偶形式
        total = len(self._input_y)
        feature_num = range(self._feature_num)
        data_num = range(total)
        gram_matrix = self._input_x.dot(self._input_x.T) # Gram 矩阵
        alpha = np.random.random(size=total) # 这里初始化alpha向量为随机值
        b = 0

        while True
            separted = True
            for i in data_num:
                inner = np.sum(alpha * self._input_y * gram_matrix[i])
                if self._input_y[i] * (inner+b) <= 0:  # 误分类点
                    separted = False
                    alpha[i] = alpha[i] + self._rate   # 对偶形式只更新alpha向量中的一个分量
                    b = b + self._rate * self._input_y[i]
            if separted:
                break
            else:
                continue
        self._final_w = (alpha * self._input_y.T).dot(self._input_x)
        self._final_b = b
        print(self._final_w, self._final_b)

input_x = [[3,3], [4,3], [1,1], [2,3]]
input_y = [1,1,-1,-1]

pla = Perceptron(input_x, 2, input_y, 1)
pla.sgd_train()
pla.pair_sgd_train()
           

或采用sklearn的iris数据集进行测试,准备的数据集如下:

# 导入相关库
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt

# 导入iris数据集
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']

# 绘制散点图
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], c='red', label='0')
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], c='green', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend();

# 取两列数据并将并将标签转化为1/-1
data = np.array(df.iloc[:100, [0, 1, -1]])
X, y = data[:,:-1], data[:,-1]
y = np.array([1 if i == 1 else -1 for i in y])
           

参考资料:

[1]https://github.com/luwill/machine-learning-code-writing

[2]https://blog.csdn.net/baidu_38172402/article/details/83583969

继续阅读