感知机算法(原始和对偶)——100%还原统计学习方法的python代码实现,每行都有注释,超清晰

参考:
统计学习方法(第二版) 李航著

目录

一、感知机的定义

二、感知机模型

三、感知机学习策略

四、感知机学习算法

4.1感知机学习算法的原始形式

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


一、感知机的定义

假设输入空间(特征空间)是\chi \subseteq \mathbb{R}^{n},输出空间是\Upsilon = \left \{ +1,-1 \right \}。输入x\in \chi表示实例的特征向量,对应与输入空间(特征空间)的点;输出y\in \Upsilon表示实例的类别。由输入空间到输出空间的如下函数:

f(x) = sign(w\cdot x+b)

称为感知机。sign是符号函数,即

sign(x)=\left\{\begin{matrix}+1, x\geq 0 \\ -1, x< 0 \end{matrix}\right.

二、感知机模型

感知机是一个二分类的线性分类器,输入为特征向量,输出为实例的类别,属于判别模型。感知机模型的假设空间是定义在特征空间中的所有线性分类模型或线性 分类器,即函数集合\left \{ f|f(x)=w\cdot x+b \right \}

通过学习得到感知机模型,对于新的输入实例给出其对应的输出类别。

三、感知机学习策略

假设训练数据是线性可分的,感知机的学习目标是求得一个能够将数据集正实例点和负实例点完全正确分开的超平面。为找出这样的超平面,需要确定感知机的模型参数   w,b,使得损失函数最小。

给定训练集

T=\left \{ (x_{1},y_{1}) ,(x_{2},y_{2}),(x_{3},y_{3}),\cdot \cdot \cdot ,(x_{N1},y_{N})\right \}

其中,x_{i}\in \chi =R^{n},y_{i}\in \Upsilon = \left \{ +1,-1 \right \}i = 1,2,3,···,N。感知机f(x) = sign(w\cdot x+b)学习的损失函数定义为:

L(w,b)=-\sum_{x_{i}\in M}^{}y_{i}(w\cdot x_{i}+b)

感知机学习的策略是在假设空间中选取使损失函数最小的模型参数w,b,即感知机模型。

四、感知机学习算法

4.1感知机学习算法的原始形式

输入:训练数据集T=\left \{ (x_{1},y_{1}) ,(x_{2},y_{2}),(x_{3},y_{3}),\cdot \cdot \cdot ,(x_{N1},y_{N})\right \},其中其中,x_{i}\in \chi =R^{n},y_{i}\in \Upsilon = \left \{ +1,-1 \right \}i = 1,2,3,···,N ;学习率 \eta (0 < \eta \leq 1) ;

输出:w,b ;感知机模型 f(x) = sign(w\cdot x+b)

  1.  选取初值w,b ;
  2. 在训练集中选取数据(x_{i},y_{i}) ;
  3. 如果y_{i}(w\cdot x_{i}+b)\leqslant 0 :   w\leftarrow w+\eta y_{i}x_{i}         b\leftarrow b+\eta y_{i}
  4. 转至2,直至训练集中没有误分类点

根据统计学习方法中的数据,得到如下的python代码

"""
感知机:
感知机是二分类的线性分类模型,输入为实例的特征向量,输出为实例的类别(+1和-1)
#0.输入数据集
#1.选取初值w、b
#2.在训练集中选取数据x_i,y_i
#3.如果y_i(w*x_i+b)<=0:
    更新权重和偏置参数  w=w+eta*y_i*x_i
                      b=b+eta*y_i
#4.转至第二步,直至训练数据集中没有误分类的点
#5.输出w,b,感知机模型
"""

# 原始形态
import numpy as np
import matplotlib.pyplot as plt


x = np.array([[3, 3], [4, 3], [1, 1]])      # 样本集
y = np.array([1, 1, -1])                    # 样本标签
w = np.array([0, 0])                        # 权重
b = 0                                       # 偏置
eta = 1                                     # 学习率
num = 0                                     # 参数更新次数
flag = 0                                    # 设计一个标志,表示是否还有误分类点 0:无误分类点 1:有误分类点


# 设计一个函数来绘制直线
def plot_line(w_p, b_p):
    x_p = np.linspace(0, 5, 10)
    if w_p[1] == 0:
        if w_p[0] == 0:
            return                                      # w_p[0]为0,什么直线都无法绘制,直接绘制散点图就行
        else:
            plt.axvline(x=-b_p, ls='--', c='green')     # w_p[1]为0,可以绘制出垂直线
    y_p = -(w_p[0] * x_p + b_p) / w_p[1]                # 两个都不为0,正常绘制,w_p[0]*x+w[1]*y+b=0 等价 w*x+b=0
    plt.plot(x_p, y_p, color='green')


while True:
    for i in range(len(x)):                         # len(x)表示多少列,能表达出样本的数量
        if y[i] * (np.inner(w, x[i]) + b) <= 0:     # 两个向量的内积 使用np.inner(x1,x2)
            w = w + eta * y[i] * x[i]               # 这里是向量中每个元素单独计算,直接使用 * 就好
            b = b + eta * y[i]
            print(f'第{num + 1}次更新:w = {w}, b = {b}')
            num = num + 1                       # 参数更新次数+1
            flag = 1                            # 有误分类点标志

            # =========可注释,不影响结果==================
            # 增强可视化,绘制出样本集和直线
            plt.figure()
            for j in range(len(x)):
                if y[j] == 1:
                    plt.plot(x[j][0], x[j][1], 'ro')  # 红点
                else:
                    plt.plot(x[j][0], x[j][1], 'bo')  # 蓝点
            plot_line(w, b)                           # 绘制出图像
            plt.show()
            # =========可注释,不影响结果==================

            break                               # 有误分类点,结束本次遍历,进入下一次遍历样本集

    if flag == 1:
        flag = 0            # 更新标志
    else:                   # 无误分点,结束循环
        break

 放出结果,与书本遍历结果一致:

第1次更新:w = [3 3], b = 1
第2次更新:w = [2 2], b = 0
第3次更新:w = [1 1], b = -1
第4次更新:w = [0 0], b = -2
第5次更新:w = [3 3], b = -1
第6次更新:w = [2 2], b = -2
第7次更新:w = [1 1], b = -3

迭代出的直线动态图:

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

输入:训练数据集T=\left \{ (x_{1},y_{1}) ,(x_{2},y_{2}),(x_{3},y_{3}),\cdot \cdot \cdot ,(x_{N1},y_{N})\right \},其中其中,x_{i}\in \chi =R^{n},y_{i}\in \Upsilon = \left \{ +1,-1 \right \}i = 1,2,3,···,N ;学习率 \eta (0 < \eta \leq 1) ;

输出:\alpha ,b ;感知机模型

 f(x) = sign(\sum_{j = 1}^{N}\alpha _{j}y_{j}x_j\cdot x+b)

其中\alpha = (\alpha _{1},\alpha _{2},\cdot \cdot \cdot,\alpha _{N} )^{T}

  1.  选取初值 \alpha ,b
  2. 在训练集中选取数据(x_{i},y_{i}) ;
  3. 如果                                                                                                                          (\sum_{j = 1}^{N}\alpha _{j}y_{j}x_j\cdot x+b)\leq 0 : \alpha _{i}\leftarrow \alpha _{i}+\eta , b\leftarrow b+\eta y_{i}
  4. 转至2,直至训练集中没有误分类点

其中\alpha_ {i}表示第i个实例点被误分的次数。

python代码如下:

"""
感知机:
感知机是二分类的线性分类模型,输入为实例的特征向量,输出为实例的类别(+1和-1)
#0.输入数据集
#1.选取初值alpha、b
#2.在训练集中选取数据x_i,y_i
#3.如果 y_i*{ (∑alpha[j]*y[j]*x[j]) * x_i + b } <= 0
    更新参数  alpha[i] = alpha[i] + eta
             b = b + eta * y_i
#4.转至第二步,直至训练数据集中没有误分类的点
#5.输出alpha,b,感知机模型
"""

# 对偶形态
import numpy as np

x = np.array([[3, 3], [4, 3], [1, 1]])      # 样本集
y = np.array([1, 1, -1])                    # 样本标签
alpha = [0, 0, 0]                           # alpha[i]表示第i个样本点误分类的次数
b = 0                                       # 偏置
eta = 1                                     # 学习率
num = 0                                     # 参数更新次数
flag = 0                                    # 设计一个标志,表示是否有误分类点 0:无误分类点 1:有误分类点

Gram = np.dot(x, x.transpose())             # np.dot计算矩阵的乘法,能算出样本之间的内积

while True:
    for i in range(len(x)):                 # len(x)表示多少列,能表达出样本的数量
        sum_alpha = 0                       # 判断是否为误分类点时,累计计算结果,每次遍历完要更新为0,避免影响下次遍历
        for j in range(len(alpha)):         # 遍历alpha,计算累计值
            sum_alpha = sum_alpha + alpha[j] * y[j] * Gram[j, i]    # Gram[j, i]表示的是x[j]与x[i]的内积
        if y[i] * (sum_alpha + b) <= 0:
            alpha[i] = alpha[i] + eta       # 更新参数
            b = b + eta * y[i]
            print(f'第{num + 1}次更新:alpha = {alpha}, b = {b}')
            num = num + 1
            flag = 1                        # 更新误分类点标志
            break
    if flag == 1:
        flag = 0                            # 更新误分类点标志
    else:
        break

 结果如下:

第1次更新:alpha = [1, 0, 0], b = 1
第2次更新:alpha = [1, 0, 1], b = 0
第3次更新:alpha = [1, 0, 2], b = -1
第4次更新:alpha = [1, 0, 3], b = -2
第5次更新:alpha = [2, 0, 3], b = -1
第6次更新:alpha = [2, 0, 4], b = -2
第7次更新:alpha = [2, 0, 5], b = -3

感知机学习算法中,给定一组特征向量(正样本和负样本)以及对应的标签,我们的目标是找到一个线性平面,使得所有正样本位于平面的一侧,而所有负样本位于另一侧。感知机对偶形式通常用于处理大型或凸优化问题,因为它可以转换为二次规划。 首先,我们需要定义一些符号: - \( w \): 感知机的权重向量,我们正在寻找的是这个向量。 - \( b \): 偏置项,也称为截距。 - \( x_i \): 样本点,对于正样本 \( x_1 = (3, 3)^T \), \( x_2 = (4, 3)^T \),对于负样本 \( x_3 = (1, 1)^T \)。 - \( y_i \): 样本的标签,对于正样本 \( y_1 = y_2 = +1 \),对于负样本 \( y_3 = -1 \)。 - \( \theta \): 对偶变量,对偶问题中的未知数。 感知机对偶问题的目标是最小化以下损失函数加上惩罚项(这里假设误分类的惩罚系数是C): \[ \max_{\theta} \sum_{i=1}^n y_i(\theta^Tx_i - b) - \frac{1}{2}\theta^T\theta \] 其中 \( n \) 是样本数量。由于 \( \theta \) 的平方项使问题变得更容易解析,我们可以通过拉格朗日乘子将其转化为对 \( w \) 和 \( b \) 的形式: \[ \min_w \frac{1}{2}w^Tw \] \[ s.t. \quad y_i(w^Tx_i - b) \geq 1, \quad i = 1, 2, ..., n \] 在这个约束条件下,每个正样本 \( x_i \) 的线性表示 \( w^Tx_i - b \) 应该大于等于1,而每个负样本小于等于1。 对于给定的三个样本点,我们可以写出相应的不等式: 1. 对于正样本 \( x_1 \) 和 \( x_2 \): - \( w^T(3, 3)^T - b \geq 1 \) - \( w^T(4, 3)^T - b \geq 1 \) 2. 对于负样本 \( x_3 \): - \( w^T(1, 1)^T - b \leq 1 \) 现在,我们可以使用这些不等式和标准的线性规划库(如GLPK、CVXOPT或Scipy)来求解这个最优化问题。但请注意,感知机学习过程可能不会收敛到全局最优解,因为它是局部最优的,并且有可能陷入局部鞍点。 在Python中,你可以使用cvxopt这样的库来进行操作,但是代码较长,不适合在这里完全展示。下面是简化的伪代码示例: ```cpp #include <cvxopt.hpp> // 定义样本和标签 std::vector<cvxopt_matrix> X = {cvxopt.matrix({3, 3}), cvxopt.matrix({4, 3}), cvxopt.matrix({1, 1})}; std::vector<double> y = {1, 1, -1}; // 定义参数和矩阵 cvxopt::matrix Q(n_samples * n_features, n_samples * n_features); cvxopt::matrix G(n_samples * n_features, n_samples); cvxopt::matrix h(n_samples); cvxopt::matrix A(n_samples, n_features); cvxopt::matrix b(n_samples); // 初始化矩阵 // ... // 设置不等式约束 // ... // 解决QP问题 cvxopt::solvers.options("glpk", "msg_lev", "GLP_MSG_OFF"); cvxopt::solvers.qp(Q, cvxopt::cvxopt_vector(G*0 + h), cvxopt::cvxopt_vector(A*0 + b)); // 提取最优权重和偏置 cvxopt::matrix w = cvxopt::solvers::primal["x"]; double b_value = cvxopt::solvers::dual["lam"]; ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值