PRML-4.1 判别函数

1.概念

判别式是一个使用输入向量x并把它分配给K种分类的其中一种Ck的函数。本章中,我们把我们的讨论局限于线性判别式(linear discriminants),即那些决策面是超平面的判别函数。为了简化讨论,我们首先考虑二分类的情况,再推广到K>2的情形。

2 二分类

线性判别式的最简单形式是取输入向量的线性函数,即
(4.4)y(x)=wTx+w0

如果y(x)0则把输入向量x分到类C1中,否则分到C2中。因此,对应的决策边界由y(x)=0确定,它对应着D维空间中的一个(D1)维超平面。

原点到决策面的标准距离由
(4.5)wTxw=w0w


解释一下
设点x在决策面上,w是法向量
wTxwxw=||w||||x||cosθw=||x||cosθ
因为x在决策面上,wTx+w0=0,wTx=w0


3 多分类

现在把线性判别式推广到K>2个类别的情况,我们可能会尝试组合多个二分类判别式来构造一个K类判别式。然而这会导致一些现在要展示的严重问题(Duda and Hart, 1973)。

考虑一个包含K1个把属于类别Ck的点与其它的区分开的分类器(本类和其他类)。这被称为一对其他(one-versus-the-rest)分类器。图4.2的左手边展示了一个涉及三个类别的例子,这种方法会导致空间中有一块类别含糊不清的区域。

另一种方法是引入K(K1)/2个二元判别函数,对每一对类别都设置一个。这被称为一对一(one-versus-one)分类器。然后根据这些判别函数的大多数投票来确定每个点的类别。然而这也会引起类别含糊不清的区域的问题,图4.2的右手边展示。

所以,引入包含K个形式为
(4.9)yk(x)=wkTx+wk0
的线性函数的单个K类判别式,并把x分入对于所有jk都有yk(x)>yj(x)的类Ck,就可以避免这些问题。类Ck,Cj间的决策边界由yk(x)=yj(x)给出,它对应形式为

(4.10)(wkwj)Tx+(wk0wj0)=0

(D1)维的超平面。这与4.1.1节讨论的二分类情况下的决策边界具有相同的形式,因此也有类似的几何性质。

这样的判别式得到的决策区域总是单连通且凸的。为了证明这个,让我们考虑图4.3中展示的,两个都位于决策区域Rk中的点xA,xB

任何一个在连接xA,xB线段上的点x^可以表示为

(4.11)x^=λxA+(1λ)xB

其中0λ1。根据判别函数的线性性,可以得到

(4.12)yk(x^)=λyk(xA)+(1λ)yk(xB)

因为xA,xB都在Rk中,所以对于所有jk都满足yk(xA)>yj(xA),yk(xB)>yj(xB),所以yk(x^)>yj(x^),所以x¯Rk中。因此Rk是单连通且凸的(我理解是如果两个点都属于第k类,它们之间的点也都是属于k类别)。
注意,对于二分类的情形,我们既可以使用这里讨论的基于两个判别函数y1(x),y2(x)方法,也可以使用4.1.1节给出基于单一的判别函数y(x)的更简单的但等价的方法。

现在,我们开始探讨三种线性判别函数的参数学习方法,即基于最小二乘、Fisher线性判别式,以及感知器算法

4.判别函数1-最小平方法法

每个类别Ck通过它自己的线性模型

(4.13)yk(x)=wkTx+wk0

可以很容易地把这些量聚集在一起表示,即
(4.14)y(x)=W~Tx~

其中

W~kthD+1w~k=(wk0,wkT)T

x~

x~x0=1(1,xT)T

训练数据集

xn,tnn=1,...,N

T,y_test

nthtnTT

X,x_test

x~nTX~

平方和误差函数就可以写成

(4.15)ED(W~)=12Tr{(X~W~T)T(X~W~T)}


证明 4.15

先详细展开一些标记
x~=(1x1.xD)D+1×1,X~=(x~1T.x~NT)N×D+1,T=(t1Tt2T.tNT)N×K,W~=(w10...wK0.........w1D...wKD)D+1×K,(wk0...wkD)=w~k,W~T=(w10...w1D.........wK0...wKD)K×D+1

y(x)=W~Tx~=(w10...w1D.........wK0...wKD)K×D+1(1x1.xD)D+1×1
y(x)=(y1(x)...yK(x))K×1=(p1(x)x1...pK(x)xK)K×1
然后开始计算
X~W~=(x~1T.x~NT)(w10...wK0.........w1D...wKD)=(p1(x1)...pk(x1).........p1(xN)...pk(xN))(y(x))=(p(c1|x1)...p(ck|x1).........p(c1|xN)...p(ck|xN))(),

X~W~T=(p(c1|x1)t11...p(ck|x1)t1K.........p(c1|xN)t1N...p(ck|xN)tNK)

X~W~T=A,aij=p(cj|ci)tij
(X~W~T)T(X~W~T)=(a11...aN1.........a1K...aKN)(a11...a1K.........aN1...aKN)=(a112+a212+...+aN12.....................a1K2+a2K2+...+aNK2)K×K

TR(ATA)=(a112+a212+...+aN12)+...+(a1K2+a2K2+...+aNK2)=C1+...+CK

证毕


令关于W~的导数等于零,整理,可得W~的解

(4.16)W~=(X~TX~)1X~TT=X~+T

其中X~+是3.1.1节中讨论过的X~的伪逆。然后我们得到形式为

(4.17)y(x)=W~Tx~=TT(X~+)Tx~

的判别函数。


多目标变量的最小二乘解的一个有趣性质是,如果训练集合中的每个目标向量对于某些常数a,b都满足线性约束

(4.18)aTtn+b=0

那么,模型对于任意的x值预测也满足这个约束,即

(4.19)aTy(x)+b=0


证明 4.18 习题4.2


python 最小二乘法分类

点击查看代码

# 二分类
x_train, y_train = create_toy_data()
x1_test, x2_test = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
x_test = np.array([x1_test, x2_test]).reshape(2, -1).T

feature = PolynomialFeature(1)
X_train = feature.transform(x_train)
X_test = feature.transform(x_test)

model = LeastSquaresClassifier()
model.fit(X_train, y_train)
y = model.classify(X_test)

plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(x1_test, x2_test, y.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
plt.xlim(-5, 5)
plt.ylim(-5, 5)
plt.gca().set_aspect('equal', adjustable='box')
plt.show()

点击查看代码

import numpy as np
from prml.linear.classifier import Classifier
from prml.preprocess.label_transformer import LabelTransformer


class LeastSquaresClassifier(Classifier):
    """
    Least squares classifier model

    X : (N, D)
    W : (D, K)
    y = argmax_k X @ W
    """

    def __init__(self, W:np.ndarray=None):
        self.W = W

    def fit(self, X:np.ndarray, t:np.ndarray):
        """
        least squares fitting for classification

        Parameters
        ----------
        X : (N, D) np.ndarray
            training independent variable
        t : (N,) or (N, K) np.ndarray
            training dependent variable
            in class index (N,) or one-of-k coding (N,K)
        """
        if t.ndim == 1:
            t = LabelTransformer().encode(t)
        self.W = np.linalg.pinv(X) @ t

    def classify(self, X:np.ndarray):
        """
        classify input data

        Parameters
        ----------
        X : (N, D) np.ndarray
            independent variable to be classified

        Returns
        -------
        (N,) np.ndarray
            class index for each input
        """
        return np.argmax(X @ self.W, axis=-1)


点击查看代码

import numpy as np


class LabelTransformer(object):
    """
    Label encoder decoder

    Attributes
    ----------
    n_classes : int
        number of classes, K
    """

    def __init__(self, n_classes:int=None):
        self.n_classes = n_classes

    @property
    def n_classes(self):
        return self.__n_classes

    @n_classes.setter
    def n_classes(self, K):
        self.__n_classes = K
        self.__encoder = None if K is None else np.eye(K)

    @property
    def encoder(self):
        return self.__encoder

    def encode(self, class_indices:np.ndarray):
        """
        encode class index into one-of-k code

        Parameters
        ----------
        class_indices : (N,) np.ndarray
            non-negative class index
            elements must be integer in [0, n_classes)

        Returns
        -------
        (N, K) np.ndarray
            one-of-k encoding of input
        """
        if self.n_classes is None:
            self.n_classes = np.max(class_indices) + 1

        return self.encoder[class_indices]

    def decode(self, onehot:np.ndarray):
        """
        decode one-of-k code into class index

        Parameters
        ----------
        onehot : (N, K) np.ndarray
            one-of-k code

        Returns
        -------
        (N,) np.ndarray
            class index
        """

        return np.argmax(onehot, axis=1)

点击查看代码

x_train, y_train = create_toy_data(add_outliers=True)
x1_test, x2_test = np.meshgrid(np.linspace(-5, 15, 100), np.linspace(-5, 15, 100))
x_test = np.array([x1_test, x2_test]).reshape(2, -1).T

feature = PolynomialFeature(1)
X_train = feature.transform(x_train)
X_test = feature.transform(x_test)

least_squares = LeastSquaresClassifier()
least_squares.fit(X_train, y_train)
y_ls = least_squares.classify(X_test)

logistic_regression = LogisticRegression()
logistic_regression.fit(X_train, y_train)
y_lr = logistic_regression.classify(X_test)

plt.subplot(1, 2, 1)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(x1_test, x2_test, y_ls.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
plt.xlim(-5, 15)
plt.ylim(-5, 15)
plt.gca().set_aspect('equal', adjustable='box')
plt.title("Least Squares")
plt.subplot(1, 2, 2)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(x1_test, x2_test, y_lr.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
plt.xlim(-5, 15)
plt.ylim(-5, 15)
plt.gca().set_aspect('equal', adjustable='box')
plt.title("Logistic Regression")
plt.show()

python 最小二乘法三分类

点击查看代码

# 三分类
x_train, y_train = create_toy_data(add_class=True)
x1_test, x2_test = np.meshgrid(np.linspace(-5, 10, 100), np.linspace(-5, 10, 100))
x_test = np.array([x1_test, x2_test]).reshape(2, -1).T

feature = PolynomialFeature(1)
X_train = feature.transform(x_train)
X_test = feature.transform(x_test)

least_squares = LeastSquaresClassifier()
least_squares.fit(X_train, y_train)
y_ls = least_squares.classify(X_test)

logistic_regression = SoftmaxRegression()
logistic_regression.fit(X_train, y_train, max_iter=1000, learning_rate=0.01)
y_lr = logistic_regression.classify(X_test)

plt.subplot(1, 2, 1)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(x1_test, x2_test, y_ls.reshape(100, 100), alpha=0.2, levels=np.array([0., 0.5, 1.5, 2.]))
plt.xlim(-5, 10)
plt.ylim(-5, 10)
plt.gca().set_aspect('equal', adjustable='box')
plt.title("Least squares")
plt.subplot(1, 2, 2)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(x1_test, x2_test, y_lr.reshape(100, 100), alpha=0.2, levels=np.array([0., 0.5, 1.5, 2.]))
plt.xlim(-5, 10)
plt.ylim(-5, 10)
plt.gca().set_aspect('equal', adjustable='box')
plt.title("Softmax Regression")
plt.show()


5-Fisher线性判别函数

我们可以从降维的角度来观察线性分类模型。首先考虑二分类的情形,并假设有D维输入向量x并使用

(4.20)y=wTx

把它投影到一维空间。如果我们在y上放一个阈值,并把yw0分到类别C1中,其余的分到类别C2,那么我们就得到了前一小节讨论的标准的线性分类器。通常来说,投影在一维空间会造成大程度的信息损失,所以能够在原来的D维空间中完全分开的样本在一维空间中可能会相互重叠。不过通过调节分量的权向量w,我们可以选择使类别最分散的一个投影。首先,考虑一个包含N1C1点和N2C2点的二分类问题,那么两个类别的均值向量为:

(4.21)m1=1N1nC1xn,m2=1N2nC2xn

当投影到w上后,最简单的度量类别之间分开程度的方法就是投影之后的的类别均值的距离。这要求我们去选择那个使得

(4.22)m2m1=wT(m2m1)

m2,m1y=wTx

最大的w的值。 其中

(4.23)mk=wTmk

类别Ck的数据投影后的均值。但是,可以通过增大w来使这个表达式无穷大。为了解决这个问题,我们现在w为单位长的,即iwi2=1。使用拉格朗日乘数法来求解限制条件下的最大化问题,得到w(m2m1)。但是这个方法还有图4.6展示的问题。


证明 w(m2m1) ,习题4.4
m2m1=wT(m2m1)
wTw=1,w(m2m1)
L(w,λ)=wT(m2m1)+λ(wTw1)
Lw=m2m1+2λw=0
w=12λ(m2m1)
w(m2m1)


图4.6,它展示了在原来二维空间(x1,x2)中可以完全分开的两个类别,当投影到连接它们的均值的直线上时,有一定程度的重叠。这是由于类别分布的协方差强非对角引起的(概率分布的协方差矩阵与对角化矩阵差距较大(暂且没有推导))。Fisher提出了最大化一种在给出大的投影均值的距离的同时,给出较小的每个类别内部的方差的函数的主意,来最小化类别重叠。(类内小,类间大

投影公式(4.20)把通过x标记的数据点转换为通过一维空间y标记的点。转换后的类别Ck的内部方差由

(4.24)sk2=nCk(ynmk)2

其中yn=wTxn。我们可以简单的定义整个数据集总的类别内部的方差为s12+s22。Fisher准则是由类别间的方差与类别内部的方差比例定义的,即

(4.25)J(w)=(m2m1)2s12+s22

使用式(4.20)、式(4.23)和式(4.24)对它重写得到

(4.26)J(w)=wTSBwwTSWw

来显示的表达对w的依赖。其中SB是类别间的协方差矩阵。SBBBetween,由

(4.27)SB=(m2m1)(m2m1)T

给出,SW是总得类别内部方差矩阵,SW,Wwithin,由

(4.28)SW=nC1(xnm1)(xnm1)T+nC2(xnm2)(xnm2)T


证明 4.26式 习题 4.5

4.20
y=wTx
4.23
mk=wTmk
4.24
sk2=nCk(ynmk)2

m1=wTm1
m2=wTm2
s12=nC1(wTxnwTm1)2
s22=nC2(wTxnwTm2)2
J(w)=(wT(m2m1))((m2m1)Tw)nC1(wT(xnm1))((xnm1)Tw)nC2(wT(xnm2))((xnm2)Tw)
SB=(m2m1)(m2m1)T
SW=nC1((xnm1))((xnm1)T)nC2((xnm2))((xnm2)T)

(4.26)J(w)=wTSBwwTSWw


对式(4.26)对于w求导,得到当

(4.29)(wTSBw)SWw=(wTSWw)SBw

使得J(w)最大。从式(4.27)我们得到SBw总是在(m2m1)的方向上。更重要的是我们只在乎w的方向,而不在乎它的大小,因此我们可以去掉标量因子(wTSBw)(wTSWw) 。在式(4.29)两边都乘以SW1得到

(4.30)wSW1(m2m1)

注意,如果类别内部的协方差是各向同性的,那么SW正比于单位矩阵,然后得到和之前讨论的一样,w正比于类均值的差。

尽管严格来说式(4.30)并不是一个判别式,而是对于数据向一维投影的方向的一个具体选择(得到方向w),但是仍习惯把它称为Fisher线性判别式。然而,投影的数据可以通过选择一个阈值y0,使得当y(x)y0时,我们把它分到C1中,否则把它分到C2,来构造后面的判别式。

例如,我们可以使用高斯概率分布对类条件密度p(y|Ck)建模,然后使用1.2.4节(高斯分布)的技术通过最大似然找到高斯分布的参数值。

当找到投影类别的高斯近似之后,1.5.1节的方法给出了最优的阈值的形式化解(最小化错误分类率)。注意,y=wTx是一组随机变量的和,因此根据中心极限定理,我们可以作出高斯分布的假设。


这里提下,在多元统计分析中,获得了方向w就可以了,偏置w0无所谓,见https://www.cnblogs.com/boyknight/p/16047144.html


4.26求导
借用公式
x(Ax)TAx(Bx)TBx=2ATAx(Bx)TBx2(xTATAx)BTBx(xTBTBx)2
wJ(w)=2SBwwTSww2(wTSBw)Sww(wTSww)2
(wTSBw)Sww=(wTSww)SBw


Fisher判别 python实现

点击查看代码

# Fisher
x_train, y_train = create_toy_data()
x1_test, x2_test = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
x_test = np.array([x1_test, x2_test]).reshape(2, -1).T

model = FishersLinearDiscriminant()
model.fit(x_train, y_train)
y = model.classify(x_test)

plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(x1_test, x2_test, y.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))
plt.xlim(-5, 5)
plt.ylim(-5, 5)
plt.gca().set_aspect('equal', adjustable='box')
plt.show()

点击查看代码

import numpy as np
from prml.linear.classifier import Classifier
from prml.rv.gaussian import Gaussian


class FishersLinearDiscriminant(Classifier):
    """
    Fisher's Linear discriminant model
    """

    def __init__(self, w:np.ndarray=None, threshold:float=None):
        self.w = w
        self.threshold = threshold

    def fit(self, X:np.ndarray, t:np.ndarray):
        """
        estimate parameter given training dataset

        Parameters
        ----------
        X : (N, D) np.ndarray
            training dataset independent variable
        t : (N,) np.ndarray
            training dataset dependent variable
            binary 0 or 1
        """
        X0 = X[t == 0]
        X1 = X[t == 1]
        m0 = np.mean(X0, axis=0)
        m1 = np.mean(X1, axis=0)
        cov_inclass = np.cov(X0, rowvar=False) + np.cov(X1, rowvar=False)
        self.w = np.linalg.solve(cov_inclass, m1 - m0)
        self.w /= np.linalg.norm(self.w).clip(min=1e-10)

        g0 = Gaussian()
        g0.fit((X0 @ self.w))
        g1 = Gaussian()
        g1.fit((X1 @ self.w))
        root = np.roots([
            g1.var - g0.var,
            2 * (g0.var * g1.mu - g1.var * g0.mu),
            g1.var * g0.mu ** 2 - g0.var * g1.mu ** 2
            - g1.var * g0.var * np.log(g1.var / g0.var)
        ])
        if g0.mu < root[0] < g1.mu or g1.mu < root[0] < g0.mu:
            self.threshold = root[0]
        else:
            self.threshold = root[1]

    def transform(self, X:np.ndarray):
        """
        project data

        Parameters
        ----------
        X : (N, D) np.ndarray
            independent variable

        Returns
        -------
        y : (N,) np.ndarray
            projected data
        """
        return X @ self.w

    def classify(self, X:np.ndarray):
        """
        classify input data

        Parameters
        ----------
        X : (N, D) np.ndarray
            independent variable to be classified

        Returns
        -------
        (N,) np.ndarray
            binary class for each input
        """
        return (X @ self.w > self.threshold).astype(np.int)


6-最小平方和Fisher判别的关系

确定线性判别式的最小二乘方法是基于使模型预测尽可能的接近目标值的目的的。相反,Fisher准则的目标是最大化输出空间中类别的区分度。这两种方法之间的关系是很有趣的。特别的,我们会证明,在二分类问题中,Fisher准则可以看成最小二乘的一个特例
目前为止,我们一直采用“1-of-K”编码来表示目标值。然而,如果我们采用一种稍微不同的编码方式,那么权重的最小二乘解会等价于Fisher判别式的解(Duda and Hart, 1973)。特别的,我们让属于C1的目标值等于N/N1,其中N1是类别C1的模式的数量,N是总的模式数量。这个目标值近似于类别C1的先验概率的倒数,同时令C2目标值等于N/N2,其中N2是类别C2的模式的数量
平方和误差函数可以写成

(4.31)E=12n=1N(wTxn+w0tn)2

分别关于w0,wE的导数,并使其等于0,得到

(4.32)n=1N(wTxn+w0tn)=0(4.33)n=1N(wTxn+w0tn)xn=0

根据式(4.32),并按选择的目标编码方式来编码tn,就可得到偏置的表示式

(4.34)w0=wTm

其中我们使用了

(4.35)n=1Ntn=N1NN1N2NN2=0

m是由

(4.36)m=1Nn=1Nxn=1N(N1m1+N2m2)

给出的全部数据的均值


说白了就是两个算法的编码方式不一样

posted @   筷点雪糕侠  阅读(265)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示