判别分析的框框很大,今天给大家写写线性判别和二次判别,这两个是判别分析中最常见也是最基本的,希望能够给大家写明白。

首先给出判别分析的定义:

Linear discriminant analysis (LDA) is a method used in statistics and other fields, to find a linear combination of features that characterizes or separates two or more classes of objects. The resulting combination may be used as a linear classifier, or, more commonly, for dimensionality reduction before later classification.

从上面的定义可以知道判别分析有两个作用,一个是降维dimensionality reduction,另一个是分类classifier。就是说这个方法可以将多维数据投射到低维平面,并且还能使得我们的数据类别非常好区分。

降维得到的,或者你可以简单的理解为降维过后的数据维度就叫做判别函数,就如下图一样,经过判别分析本来有很多特征的原始数据就只剩下几个判别函数了。


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_后验概率



 


维数灾难curse of dimensionality

有同学要好奇了,我为什么要降维呢?

首先数据维度过高(预测变量过多)存在的首要问题就是很多维度并不能给模型提供有用的信息,甚至会干扰模型表现,当维数增加,数据会变得稀疏,就是同一个数据,维度越高个案之间的距离会越远,就像下图中示意的一样:

for the same number of cases in a dataset, if you increase the feature space, the cases get further apart from each other, and there is more empty space between them


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_02


 


你可以看到数据随着维度变高变得越来越稀疏,不利于特征工程,也更容易造成过拟合。

判别分析的原理

想象一下,我现在有2个变量,我想通过这两个变量将我的样本分为2类,那么LDA要做的就是找到数据的新的表示方法,也就是将数据降维(通过投影实现),降维后找到一条可以最好地分割两个类别的线(判别函数)。

那么我们的思路是先确定每个类别的中点,然后让下面这个式子的取值最大

也就是说,我们希望两个类别均值的差异越大越好,希望两个类别的组内变异越小越好。


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_数据_03


 


就像下面这个图显示的一样,我们把黑绿两个类别都投影到图中的斜线上,就形成了图的右边部分,这个时候上面式子的值是最大的,


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_后验概率_04


 


那么有的同学会问,投影过后我只让两个类别中点距离最大行不行呢?

看下图,图中左边的投影线保证了两个类别的中点的距离最大,但是这条投影线并不如右图的判别效果好。所以LDA的追求一定是要两个类别均值的差异越大越好,希望两个类别的组内变异越小越好。


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_05


 


我们将找到的这个投影后能够最好区分两个类别的投影线叫做判别线discriminant function,这个判别线是原始变量的线性函数:


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_06


 


这个线性函数的系数就叫做典型判别函数系数canonical discriminant function coefficients,

这个系数越大说明这个变量的判别作用越强。

上面给大家写的是最简单的二分类线性判别,我们再进一步,当要判别的类别大于2类怎么办呢?接着往下看:

当我们的类别是多类时(这儿用3类举例),此时我们依然是将数据投影到低维平面上,这个时候我们希望每一类与整个数据的中点的距离最大(数据中点就是下图中的X)

it maximizes the separation between each class centroid and the grand centroid of the data (the centroid of all the data, ignoring class membership)


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_07


 


算法是怎么做的呢?

LDA first finds the axis that best separates the class centroids from the grand centroid that minimizes the variance of each class along it. Then, LDA constructs a second DF that is orthogonal to the first. This simply means the second DF must be perpendicular to the first (at a right angle in this 2D example).

首先就是找整个数据的中心,然后找到一个轴,这个轴我们叫轴A,所有的数据投影到轴A上这个A轴可以保证很好区分不同类和全类的中心而且保证每一类内的变异最小,然后再找到与轴A垂直的轴B,那么AB轴就是我们要找的能够最好区分3个类别的轴了,这个AB也就是我们的两个判别函数,大概就是这么一个过程。

再给大家继续拓展,想想我们如果有3个或者3个以上的预测变量怎么办呢?

下图就是这么一个例子,我们有xyz3个预测变量,数据有3个类别:

 


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_数据_08


 


此时,判断分析要做的就是先找到一个可以最好地区分各个类别和整个数据中点的同时还能最小化类内变异的判别函数,然后还需要一个判别函数,第二个判别函数和第一个垂直(如上图最右侧),那么通过这两个判别函数的判别分,我们就可以将数据很好地进行分类了。

上面这个3维的数据的判别思想就是将三维转化为2维,相应地我们再拓展,如果是一个3分类问题,就算你有1000个预测变量,最终判别函数也只有2,这个就是判别分析厉害的地方:

大家一定记住,判别函数的个数为类别减1和预测变量个数中比较小的哪一个。

the number of DFs is the smaller of the number of classes minus 1 or the number of predictors

曲线判别QDA

上面写的都是线性判别LDA,LDA在数据在各个预测变量上服从正态分布的情况下表现好点,而且LDA认为类别之间的协变差不多,但是实际上,我们的数据不同类别之间预测变量的协变通常不一样,这个时候考虑曲线判别就会更好。

LDA assumes that for each class in the dataset, the predictor variables covary with each other the same amount

好多同学到这儿就一脸懵逼,啥子叫预测变量的协变啊

看下图,

左侧的就是2个类别的数据,协变相同的意思就是这两个类别在预测变量上的变化一致,就是说在类别A中变量1和2的关系是β,那么同样的在类别B变量1和2的关系与β也不会差很远,就是这个意思。

下图的右侧就是中间的数据投影到曲线判别函数上和线性判别函数上的区别,大家可以看到同样的数据投影到曲线判别上判别的效果更好(投影过后两类别间距更大)


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_数据_09


 


类间距更大就意味着判别更容易结果也更准确,所以这个时候选择2次判别就会更好。

用判别分析做预测

判别分析可以将我们的很多的预测变量降到2个,那么这个算法是如何帮助我们对新数据进行预测的呢?

这儿给大家再补充一个经典的知识点:贝叶斯法则Bayes’ rule

贝叶斯法则可以帮助我们回答:我们已经知道我们这个个案中有哪些预测变量,那么这个个案属于K类的概率,就是说贝叶斯法则可以帮助我们求p(k|x)(这个叫做后验概率),这个K代表的是类别,x代表的是预测变量,就是我知道了预测变量的情况下我这个个案的类别是啥。

是啥呢?就是下面这个式子:


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_数据_10


 


我一项一项地给大家解释这个式子的右边:首先p(x|k)是第k类中预测变量x出现的概率,这个叫做似然likelihood

p(k)就是你这整个样本中k类别的概率,叫做先验概率prior probability,p(x)就是你这个整个数据中有预测变量x的个案的比例,或者是观察到有x预测变量的个案的概率,这个叫做证据evidence

有时候你想从整体中获得证据p(x)其实是很难的,如果我们把证据p(x)略去,贝叶斯法则就成了下面这个样子,就是说似然和先验概率的积越大则我们要求的后验概率就越大,那么其实我们就可以根据p(x k) × p(k)的大小对类别进行判别了嘛。


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_11


 


所以大家记住:后验概率和似然与先验概率的积成正比,我们可以根据这个积也就是后验概率来划分类别。

先验概率(p(k))就是我们数据中K个类别所占的比例,似然p(x|k)就是每个类别中出现目标预测变量x的概率,这两个东西都是非常容易从数据中得到的,所以对我们的分类问题贝叶斯法则很好用。

那么对于我们的例子,似然怎么做呢,我们把数据都投影到判别函数上然后估计数据的概率密度,就像下图的左边,我们把数据都映射到判别函数上,每一类都有相应的概率密度,所谓的概率密度就是观测某种判别分的个案的概率。

就是说每一类都有相应判别分出现的概率,不就是似然嘛,不就是p(x| k)嘛,有了似然,再结合先验,我们就可以推断后验了嘛,这个就是判别分析做预测或者叫做判别的原理。

 


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_后验概率_12


 


我们得到了每一个个案的概率密度之后就可以用贝叶斯法则来估计后验概率了,然后哪一个类别的后验概率最大,我们就认为这个个案属于这个类别。

实例操练

我们要用到的数据为wine dataset,我们要做的事情为通过红酒的特征预测红酒的类别,我们的数据大概长这样,可以看到我们的可用的预测变量很多(除了class全是预测变量),因为预测变量很多,所以拥有降维功能的判别分析就是一个很可取的方法:


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_13


 


接下来我们就开始进行LDA模型的训练,训练过程依然是3步,首先定义任务,然后定义学习器,然后学习,整体代码如下:

<span style="color:#222222"><code>wineTask <- makeClassifTask(<span style="color:#114ba6">data</span> = wineTib, target = <span style="color:#00753b">"Class"</span>)
lda <- makeLearner(<span style="color:#00753b">"classif.lda"</span>)
ldaModel <- train(lda, wineTask)</code></span>

运行上面的代码我们的LDA模型就训练好了,我们可以用getLearnerModel()函数提取出模型参数,我们也可以用predict()做预测,这个函数再LDA中预测出来的是判别分:

<span style="color:#222222"><code><span style="color:#114ba6">ldaModelData</span> <- getLearnerModel(ldaModel)
ldaPreds <- predict(ldaModelData)<span style="color:#d96322">$x</span>
head(ldaPreds)</code></span>

结果如下图:


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_14


 


也就是说我们通过这两个判别函数就可以非常好滴将我们的红酒区分出来类别,我们也成功地将很多个预测变量转换为了仅仅两个判别函数,这个就是判别分析的魔力。

我可以将判别分反应出的类别信息可视化出来:

<span style="color:#222222"><code>wineTib %>%
  mutate(LD1 = ldaPreds[, <span style="color:#a82e2e">1</span>],
         LD2 = ldaPreds[, <span style="color:#a82e2e">2</span>]) %>%
  ggplot(aes(LD1, LD2, col = Class)) +
  geom_point() +
  stat_ellipse() +
  theme_bw()</code></span>

 


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_后验概率_15


 


看见没,清晰且直观。

上面是LDA的实例操练,我们继续用同样的数据来做一个QDA,代码也非常简单哈,就是把学习器一换就可以了:

<span style="color:#222222"><code><span style="color:#114ba6">qda</span> <- makeLearner(<span style="color:#00753b">"classif.qda"</span>)
qdaModel <- train(qda, wineTask)</code></span>

模型的交叉验证

训练好模型之后,必不可少的交叉验证,本例中我们用10折验证:

<span style="color:#222222"><code>kFold <span style="color:#00753b"><- makeResampleDesc(method = "RepCV", folds = 10, reps = 50,</span>
stratify = <span style="color:#00753b">TRUE)</span>
ldaCV <span style="color:#00753b"><- resample(learner = lda, task = wineTask, resampling = kFold,</span>
measures = <span style="color:#00753b">list(mmce, acc))</span>
qdaCV <span style="color:#00753b"><- resample(learner = qda, task = wineTask, resampling = kFold,</span>
measures = <span style="color:#00753b">list(mmce, acc))</span></code></span>

运行上面的代码就可以得到模型的准确率,可以看到,QDA的准确率达到了99.2%,非常恐怖的数据了:


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_后验概率_16


 


我们做出来混淆矩阵看看哪一类错的比较多:

<span style="color:#222222"><code>calculateConfusionMatrix(ldaCV$pred, relative = <span style="color:#114ba6">TRUE</span>)</code></span>

 


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_数据_17


 


可以看到好像第三类错的比较多一些,但是总体表现已经非常恐怖了。经过交叉验证,我们发现我们的模型还是表现比较好的,接下来给大家写如何利用我们训练好的模型预测新的数据。

用判别分析做预测

预测用到的函数依然是predict函数,我们直接把要预测的个案喂给模型就可以,例子如下:

<span style="color:#222222"><code>newcase <span style="color:#114ba6"><- tibble(Alco = <span style="color:#00753b">13,</span> Malic = <span style="color:#00753b">2,</span> Ash = <span style="color:#00753b">2.2,</span> Alk = <span style="color:#00753b">19,</span> Mag = <span style="color:#00753b">100,</span>
                   Phe = <span style="color:#00753b">2.3,</span> Flav = <span style="color:#00753b">2.5,</span> Non_flav = <span style="color:#00753b">0.35,</span> Proan = <span style="color:#00753b">1.7,</span>
                   Col = <span style="color:#00753b">4,</span> Hue = <span style="color:#00753b">1.1,</span> OD = <span style="color:#00753b">3,</span> Prol = <span style="color:#00753b">750)</span>

predict(qdaModel, newdata = <span style="color:#00753b">newcase)</span></span></code></span>

运行上面的代码,模型就会输出我们的响应结果:


LDA分析的解释度怎么写在坐标轴 r语言 r语言lda函数输出含义_判别分析_18


 


可以看到这个新的case被我们的模型认为是属于1类的,到这儿训练和预测都给大家写完了。

小结

今天给大家写了判别分析的原理与做法,包括显性判别LDA和二次判别QDA,感谢大家耐心看完,自己的文章都写的很细