⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议。
Skip to content

第2章 感知机

习题2.1

  Minsky 与 Papert 指出:感知机因为是线性模型,所以不能表示复杂的函数,如异或 (XOR)。验证感知机为什么不能表示异或。

解答:

解答思路:

  1. 列出异或函数(XOR)的输入和输出;
  2. 使用图例法证明异或问题是线性不可分的;
  3. 使用反证法证明感知机无法表示异或。

解题步骤:

第1步:异或函数(XOR)的输入和输出

  对于异或函数(XOR),全部的输入与对应的输出如下:

x1x2y=x1x2
00-1
011
101
11-1

第2步:使用图例法证明异或问题是线性不可分的

python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# 使用Dataframe表示异或的输入与输出数据
x1 = [0, 0, 1, 1]
x2 = [0, 1, 0, 1]
y = [-1, 1, 1, -1]
x1 = np.array(x1)
x2 = np.array(x2)
y = np.array(y)
data = np.c_[x1, x2, y]
data = pd.DataFrame(data, index=None, columns=['x1', 'x2', 'y'])
data.head()
x1x2y
000-1
1011
2101
311-1
python
# 获取正类别(y=1)的数据
positive = data.loc[data['y'] == 1]
# 获取负类别(y=-1)的数据
negative = data.loc[data['y'] == -1]

# 绘制数据图
# 绘制坐标轴
plt.xlim(-0.5, 1.5)
plt.ylim(-0.5, 1.5)
plt.xticks([-0.5, 0, 1, 1.5])
plt.yticks([-0.5, 0, 1, 1.5])
# 添加坐标轴文字
plt.xlabel("x1")
plt.ylabel("x2")
# 绘制正、负样本点
plt.plot(positive['x1'], positive['x2'], "ro")
plt.plot(negative['x1'], negative['x2'], "bx")
# 添加图示
plt.legend(['Positive', 'Negative'])
plt.show()

png

  从上图可以看出,无法使用一条直线将两类样本分开,所以异或问题是线性不可分的

python
from sklearn.linear_model import Perceptron
import numpy as np

# 构造异或问题的训练数据集
X_train = np.array([[1, 1], [1, 0], [0, 1], [0, 0]])
y = np.array([-1, 1, 1, -1])

# 使用sklearn的Perceptron类构建感知机模型
perceptron_model = Perceptron()
# 进行模型训练
perceptron_model.fit(X_train, y)

# 打印模型参数
print("感知机模型的参数:w=", perceptron_model.coef_[
      0], "b=", perceptron_model.intercept_[0])
感知机模型的参数:w= [0. 0.] b= 0.0

  上述使用sklearn的Perceptron类构建感知机模型,从模型的参数上观察,感知机模型无法表示异或。

第3步:使用反证法证明感知机无法表示异或

  根据书中第2章感知机模型的定义2.1:

定义2.1(感知机) 假设输入空间(特征空间)是XRn,输出空间是y={+1,1}。输入xX表示实例的特征向量,对应于输入空间(特征空间)的点;输出yY表示实例的类别。由输入空间到输出空间的如下函数:

f(x)=sign(wx+b)

称为感知机。其中,wb为感知机模型参数,wRn叫做权值或权值向量,bR叫做偏置,wx表示wx的内积。sign是符号函数,即

sign(x)={+1,x01,x<0

  假设感知机模型可以表示异或问题,即满足异或函数(XOR)输入与输出的情况(见第1步)。假设x向量只有两个维度x1x2

  1. 根据x1=0,x2=0,f(x)=1,则wx+b<0,可得b<0
  2. 根据x1=0,x2=1,f(x)=1,则w2+b>0,结合b<0,可得w2>b>0
  3. 根据x1=1,x2=0,f(x)=1,则w1+b>0,结合b<0,可得w1>b>0
  4. 根据x1=1,x2=1,并结合w1+b>0w2>0,则w1+w2+b>0,可得f(x)=1,与异或条件中的f(x)=1矛盾。

  所以假设不成立,原命题成立,即感知机模型不能表示异或。

习题2.2

  模仿例题 2.1,构建从训练数据求解感知机模型的例子。

解答:

解答思路:

  按照书中第2章感知机学习算法2.1,编写代码并绘制分离超平面

算法2.1(感知机学习算法的原始形式)
输入:训练数据集T={(x1,y1),(x2,y2),,(xN,yN)},其中xiX=RnyiY={1,+1}i=1,2,,N;学习率η(0<η1)
输出:w,b;感知机模型f(x)=sign(wx+b)
(1)选取初值w0,b0
(2)在训练集中选取数据(xi,yi)
(3)如果yi(wxi+b)0

ww+ηyixibb+ηyi

(4)转至(2),直至训练集中没有误分类点。

解题步骤:

python
import numpy as np
from matplotlib import pyplot as plt
%matplotlib tk


class Perceptron:
    def __init__(self, X, Y, lr=0.001, plot=True):
        """
        初始化感知机
        :param X: 特征向量
        :param Y: 类别
        :param lr: 学习率
        :param plot: 是否绘制图形
        """
        self.X = X
        self.Y = Y
        self.lr = lr
        self.plot = plot
        if plot:
            self.__model_plot = self._ModelPlot(self.X, self.Y)
            self.__model_plot.open_in()

    def fit(self):
        # (1)初始化weight, b
        weight = np.zeros(self.X.shape[1])
        b = 0
        # 训练次数
        train_counts = 0
        # 分类错误标识
        mistake_flag = True
        while mistake_flag:
            # 开始前,将mistake_flag设置为False,用于判断本次循环是否有分类错误
            mistake_flag = False
            # (2)从训练集中选取x,y
            for index in range(self.X.shape[0]):
                if self.plot:
                    self.__model_plot.plot(weight, b, train_counts)
                # 损失函数
                loss = self.Y[index] * (weight @ self.X[index] + b)
                # (3)如果损失函数小于0,则该点是误分类点
                if loss <= 0:
                    # 更新weight, b
                    weight += self.lr * self.Y[index] * self.X[index]
                    b += self.lr * self.Y[index]
                    # 训练次数加1
                    train_counts += 1
                    print("Epoch {}, weight = {}, b = {}, formula: {}".format(
                        train_counts, weight, b, self.__model_plot.formula(weight, b)))
                    # 本次循环有误分类点(即分类错误),置为True
                    mistake_flag = True
                    break
        if self.plot:
            self.__model_plot.close()
        # (4)直至训练集中没有误分类点
        return weight, b

    class _ModelPlot:
        def __init__(self, X, Y):
            self.X = X
            self.Y = Y

        @staticmethod
        def open_in():
            # 打开交互模式,用于展示动态交互图
            plt.ion()

        @staticmethod
        def close():
            # 关闭交互模式,并显示最终的图形
            plt.ioff()
            plt.show()

        def plot(self, weight, b, epoch):
            plt.cla()
            # x轴表示x1
            plt.xlim(0, np.max(self.X.T[0]) + 1)
            # y轴表示x2
            plt.ylim(0, np.max(self.X.T[1]) + 1)
            # 画出散点图,并添加图示
            scatter = plt.scatter(self.X.T[0], self.X.T[1], c=self.Y)
            plt.legend(*scatter.legend_elements())
            if True in list(weight == 0):
                plt.plot(0, 0)
            else:
                x1 = -b / weight[0]
                x2 = -b / weight[1]
                # 画出分离超平面
                plt.plot([x1, 0], [0, x2])
                # 绘制公式
                text = self.formula(weight, b)
                plt.text(0.3, x2 - 0.1, text)
            plt.title('Epoch %d' % epoch)
            plt.pause(0.01)

        @staticmethod
        def formula(weight, b):
            text = 'x1 ' if weight[0] == 1 else '%d*x1 ' % weight[0]
            text += '+ x2 ' if weight[1] == 1 else (
                '+ %d*x2 ' % weight[1] if weight[1] > 0 else '- %d*x2 ' % -weight[1])
            text += '= 0' if b == 0 else ('+ %d = 0' %
                                          b if b > 0 else '- %d = 0' % -b)
            return text
python
X = np.array([[3, 3], [4, 3], [1, 1]])
Y = np.array([1, 1, -1])
model = Perceptron(X, Y, lr=1)
weight, b = model.fit()
Epoch 1, weight = [3. 3.], b = 1, formula: 3*x1 + 3*x2 + 1 = 0
Epoch 2, weight = [2. 2.], b = 0, formula: 2*x1 + 2*x2 = 0
Epoch 3, weight = [1. 1.], b = -1, formula: x1 + x2 - 1 = 0
Epoch 4, weight = [0. 0.], b = -2, formula: 0*x1 - 0*x2 - 2 = 0
Epoch 5, weight = [3. 3.], b = -1, formula: 3*x1 + 3*x2 - 1 = 0
Epoch 6, weight = [2. 2.], b = -2, formula: 2*x1 + 2*x2 - 2 = 0
Epoch 7, weight = [1. 1.], b = -3, formula: x1 + x2 - 3 = 0

png

习题2.3

  证明以下定理:样本集线性可分的充分必要条件是正实例点所构成的凸壳与负实例点所构成的凸壳互不相交。

解答:

解答思路:

  1. 写出凸壳和线性可分的定义
  2. 证明必要性:线性可分凸壳不相交
  3. 证明充分性:凸壳不相交线性可分

第1步:凸壳与线性可分的定义

  1. 根据书中第2章习题脚注1的凸壳定义如下:

设集合SRn,是由Rn中的k个点所组成的集合,即S={x1,x2,,xk}。定义S的凸壳conv(S)为:

conv(S)={x=i=1kλixi|i=1kλi=1,λi0,i=1,2,,k}
  1. 根据书中第2章数据集的线性可分性定义2.2:

定义2.2(数据集的线性可分性) 给定一个数据集

T={(x1,y1),(x2,y2),,(xn,yn)}

其中xiX=Rn,yiY={+1,1},i=1,2,,n,如果存在某个超平面S

wx+b=0

能够将数据集的正实例点和负实例点完全正确划分到超平面的两侧,即对所有yi=+1的实例i,有wxi+b>0,对yi=1的实例i,有wxi+b<0,则称数据集T为线性可分数据集,否则称数据集T线性不可分。

第2步:证明必要性:线性可分凸壳不相交

证明思路(反证法):

  1. 假设原命题不成立:样本集线性可分,正实例点所构成的凸壳与负实例点所构成的凸壳相交
  2. 条件推理
  3. 发现矛盾,得出原命题成立

证明步骤:

  1. 假设原命题不成立:
    设数据集T中的正例点集为S+S+的凸壳为conv(S+),负实例点集为SS的凸壳为conv(S)
    假设样本集线性可分,正实例点所构成的凸壳与负实例点所构成的凸壳相交,即存在某个元素s,同时满足sconv(S+)sconv(S)

  2. 条件推理:
    若数据集T是线性可分的,根据线性可分的定义,则存在一个超平面能够将S+S完全分离:

wx+b=0

对于所有的正例点xi,有

wxi+b=εi>0,i=1,2,,|S+|

根据凸壳的定义,对于conv(S+)中的元素s+,有

ws++b=w(i=1|S+|λixi)+b=(i=1|S+|λi(εib))+b=i=1|S+|λiεi(bi=1|S+|λi)+b(i=1|S+|λi=1)=i=1|S+|λiεi

因此ws++b=i=1|S+|λiεi>0
同理对于S中的元素s,有ws+b=i=1|S|λiεi<0

  1. 找出矛盾,得出原命题成立:
    根据条件推理,当sconv(S+)ws+b=i=1|S+|λiεi>0,当sconv(S)ws+b=i=1|S|λiεi<0,既s不可能同时满足若sconv(S+)sconv(S),这与假设命题矛盾。

因此,原命题成立,当样本线性可分时,conv(S+)conv(S)必不相交。必要性得证。

第3步:证明充分性:凸壳不相交线性可分

证明思路:

  1. 根据凸壳不相交,找到一个超平面
  2. 证明这个超平面可将两个互不相交的凸壳分隔开(反证法)
  3. 上述超平面可以将凸壳分隔开,则样本集满足线性可分

证明步骤:

  1. 根据凸壳不相交,找到一个超平面:
    设数据集T中的正例点集为S+S+的凸壳为conv(S+),负实例点集为SS的凸壳为conv(S),且conv(S+)conv(S)不相交。
    定义两个点x1,x2的距离为
dist(x1,x2)=x1x22

定义conv(S+)conv(S)的距离是,分别处于两个凸壳集合中的点的距离最小值:

dist(conv(S+),conv(S))=mins+s2s+conv(S+),sconv(S)

记最小值点分别为x+,x,即:

dist(conv(S+),conv(S))=dist(x+,x)x+conv(S+),xconv(S)

定义以(x+,x)为法线,且过两点中点的超平面为f(x|w,b)=0,则参数为:

f(x|w,b)=(x+x)T(xx++x2){w=(x+x)Tb=12(x+22x22)
  1. 证明这个超平面可将两个互不相交的凸壳分隔开(反证法)
    若某个超平面可将两个互不相交的凸壳分隔开,则f(x)0,xconv(S+)f(x)0,xconv(S)
f(x)=(x+x)T(xx++x2)=(x+x)T(x+x+x+x++x2)=(x+x)T(xx++x+x2)=(x+x)T(xx+)+x+x222

假设原命题不成立:当xconv(S+)时,假设f(x)<0,则有:

(x+x)T(xx+)<0

设点u=x++t(xx+),t[0,1],即ux+x的线段上。根据凸壳定义,uconv(S+)。则ux距离的平方为:

g(t)=ux22=x++t(xx+)x22

求解ux距离的最小值,对上式求导:

g(t)=2(x++t(xx+)x)(xx+)=2(x+x)T(xx+)+txx+22

根据假设,在t=0时,得g(t)<0。在当t足够接近于0时(导函数在0处的极限值为负,则存在邻域函数递减),即g(t)<g(0)
存在一点u,使得它到x的距离,比定义的凸壳距离dist(x+,x)还小。产生矛盾。
故原命题成立,即f(x)0,xconv(S+)。同理,可证f(x)0,xconv(S)。则可以找到一个超平面将两个互不相交的凸壳分隔开。

  1. 上述超平面可以将凸壳分隔开,则样本集满足线性可分
      根据凸壳定义,数据集T中正例点s+conv(S+),负例点sconv(S)。上述超平面可以将正例点集S+和负例点集S两个凸壳分隔开,则可以使样本集线性可分。充分性得证。