阅读本文需要的背景知识点:线性判别分析、一丢丢编程知识

一、引言

  前面两节介绍了线性判别分析在不同角度下的实现方式,一种是根据费舍尔“类内小、类间大”的角度,另一种则是从概率分布的角度。本节来介绍另一种判别分析——二次判别分析算法1(Quadratic Discriminant Analysis Algorithm / QDA)

二、模型介绍

  同线性判别分析一样,从概率分布的角度来得到二次判别分析,区别在于线性判别分析假设每一种分类的协方差矩阵相同,而二次判别分析中每一种分类的协方差矩阵不同。

(1)同线性判别分析一样,我们的目的就是求在输入为 x 的情况下分类为 k 的概率最大的分类,所以我们可以写出假设函数如下图(1)式
(2)对其概率取对数,不影响函数的最后结果
(3)带入上面的 二次判别分析python 二次判别分析interaction_二次判别分析python 的表达式,由于 二次判别分析python 二次判别分析interaction_机器学习_02 对最后结果也没有影响,也可以直接去掉
(4)带入多元正态分布的概率密度函数表达式,注意这里与线性判别分析的不同,协方差矩阵在每一种类型下是不同的
(5)将(4)式中的对数化简得到
(6)这时就不能和线性判别分析一样去掉第二项了,而是要保留其中协方差矩阵行列式的部分,得到最后的结果
二次判别分析python 二次判别分析interaction_二次判别分析python_03

  观察上面的(6)式,可知是关于 x 的二次函数,所以这也是该算法被称为二次判别分析算法的原因。

三、代码实现

使用 Python 实现二次判别分析(QDA):

def qda(X, y):
   """
   二次判别分析(QDA)
   args:
       X - 训练数据集
       y - 目标标签值
   return:
       y_classes - 标签类别
       priors - 每类先验概率
       means - 每类均值向量
       sigmags - 每类协方差矩阵
       dets - 每类协方差矩阵行列式
   """
   # 标签值
   y_classes = np.unique(y)
   # 每类先验概率
   priors = []
   # 每类均值向量
   means = []
   # 每类协方差矩阵
   sigmags = []
   # 每类协方差矩阵行列式
   dets = []
   for idx in range(len(y_classes)):
       c = X[y==y_classes[idx]][:]
       # 先验概率
       prior = c.shape[0] / X.shape[0]
       priors.append(prior)
       # 均值向量
       mu = np.mean(c, axis=0)
       means.append(mu)
       # 协方差矩阵
       sigma = c - mu
       sigma = sigma.T.dot(sigma) / c.shape[0]
       sigmags.append(np.linalg.pinv(sigma))
       # 协方差矩阵行列式
       dets.append(np.linalg.det(sigma))
   return y_classes, priors, means, sigmags, dets

def discriminant(X, y_classes, priors, means, sigmags, dets):
   """
   判别新样本点
   args:
       X - 数据集
       y_classes - 标签类别
       priors - 每类先验概率
       means - 每类均值向量
       sigmags - 每类协方差矩阵
       dets - 每类协方差矩阵行列式
   return:
       分类结果
   """
   ps = []
   for idx in range(len(y_classes)):
       x = X - means[idx]
       p = - 0.5 * (np.sum(np.multiply(x.dot(sigmags[idx]), x), axis=1) + np.log(dets[idx])) + priors[idx]
       ps.append(p)
   return y_classes.take(np.array(ps).T.argmax(1))

四、第三方库实现

scikit-learn2 实现线性判别分析:

from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis

# 初始化二次判别分析器
qda = QuadraticDiscriminantAnalysis()
# 拟合数据
qda.fit(X, y)
# 预测数据
qda.predict(X)

  sklearn 的实现并没有像上面的实现一样直接去计算协方差矩阵的逆矩阵,而是通过奇异值分解(SVD)的方式避免直接求协方差矩阵的逆矩阵,计算复杂度会小很多,具体可参考 sklearn 文档3 中对协方差矩阵的估计算法。

五、示例演示

  下图展示了存在二种分类时的演示数据,其中红色表示标签值为 0 的样本、蓝色表示标签值为 1 的样本:

二次判别分析python 二次判别分析interaction_二次判别分析python_04

  下面两张图分别展示了线性判别分析和二次判别分析拟合数据的结果,其中浅红色表示拟合后根据权重系数计算出预测值为 0 的部分,浅蓝色表示拟合后根据权重系数计算出预测值为 1 的部分:

二次判别分析python 二次判别分析interaction_机器学习_05


二次判别分析python 二次判别分析interaction_算法_06

  可以很明显的看到两种判别分析的决策边界的不同,线性判别分析只能学习线性边界,而二次判别分析可以学习二次边界,因此具有更大的灵活性。

六、思维导图

二次判别分析python 二次判别分析interaction_算法_07