鸢尾花数理统计大作业:PCA/LDA、GMM与马氏距离全流程

发布时间:2026/11/4 16:39:33
鸢尾花数理统计大作业:PCA/LDA、GMM与马氏距离全流程

简介:围绕鸢尾花数据集展开的数理统计大作业文档,面向正在学习数理统计、机器学习课程的学生及需要完成同类课程作业的读者。内容以花萼长度、花萼宽度、花瓣长度、花瓣宽度四个属性为基础,串联马氏距离、混合高斯模型、主成分分析、线性判别分析与刀切法等知识点,覆盖数据预处理、降维去噪、聚类分类及模型评估的完整分析流程。压缩包内为1个docx文档,约511KB,集中呈现算法原理、符号说明、数据处理与结果比对,便于按章节梳理思路。该作业已有1429人学习,可作为课程大作业的结构参考、方法对照与复习材料,帮助读者理解各统计方法在鸢尾花数据上的适用场景、参数估计思路以及降维方法之间的差异,并借鉴刀切法评估模型泛化能力的做法。

1. 从一朵鸢尾花说起:这份数理统计大作业到底在算什么

量一下花瓣多长、多宽,花萼多长、多宽,四个数字填进去,猜这朵花是三个品种里的哪一个。这听起来像植物学入门题,其实是很多数理统计课程的第一份大作业。这份围绕鸢尾花展开的数理统计大作业,把四列看起来很朴素的测量数据,走完了一条完整的分析流水线:先用主成分分析和线性判别分析把四维压到二维甚至一维,再用高斯混合模型拟合每一类的分布形状,用马氏距离判断新样本离哪一类更近,最后用刀切法把降维方法和降维程度挨个比一遍。名词一箩筐,拆开看,每个都在回答很朴素的问题——数据太挤怎么摊开、一堆点怎么描出轮廓、两个点怎么算远近、结论可不可信。搞懂这条链路,比背十个公式有用。

2. 鸢尾花数据集为什么成了经典:150 行数据撑起的一门课

2.1 四个属性、三个答案:这份数据长什么样

一排数字摆在你面前:5.1、3.5、1.4、0.2。没有表头,你会以为这是什么参数。补上单位就清楚了——花萼长 5.1 厘米、花萼宽 3.5 厘米、花瓣长 1.4 厘米、花瓣宽 0.2 厘米,这是一朵鸢尾花的体检报告。整份数据集就是 150 份这样的报告,每一份后面跟着一个答案:它属于三个品种中的哪一个。

列含义类型单位
属性一花萼长度数值型厘米
属性二花萼宽度数值型厘米
属性三花瓣长度数值型厘米
属性四花瓣宽度数值型厘米
标签品种类别,记作 0 / 1 / 2类别型无

三个品种各 50 条,标签记作 0、1、2,四列都是数值型,单位统一为厘米。这点很重要,后面讲标准化和马氏距离时会看到,量纲统一能省掉不少麻烦。

任务本身很直白:给四个数,判断类别。用统计的说法,四列是自变量、品种是因变量;用机器学习的说法,这是一个四维输入、三分类输出的监督学习问题。两种说法指向同一件事,只是词汇系统不同。

2.2 它凭什么被反复使用:一把公认的尺子

1936 年,一位统计学家整理发表了这组数据,用来演示多变量判别的方法;到 1988 年,它被放进公开的数据仓库,从此成了数理统计和机器学习里出场率最高的数据之一。近九十年过去,它仍然排在各类入门数据集的推荐列表前列。

为什么是它?两个原因。第一,它足够小。150 条、4 列,拿张纸按几下计算器就能把手算流程走一遍,写代码也不用等太久,调试时一眼能看出哪一步算错。第二,它又足够不平凡。三个品种里,有一类靠花瓣尺寸就能和另外两类拉开距离,剩下两类在边界上互相咬合,谁也没法用一条直线干净利落地切开。这种“大部分好分、小部分难分”的结构,恰好能拉开不同方法的差距——太简单的数据,什么算法都拿满分,比不出高低;太难的数据,什么算法都出问题,也看不出优劣。

这就是经典数据集的价值:它像一把被很多人校准过的尺子。你拿自己的方法量出一个数,虽然不能说明一切,但至少有一个公认的参照。这份大作业选它,道理也在这——重点不在数据本身多复杂,而在于用同一份数据把一整套方法串起来,看它们在同等条件下各自表现如何。

补充一个常被忽略的细节:鸢尾花的四个属性之间存在明显的线性关系,花瓣长的往往花瓣也宽,花萼尺寸和花瓣尺寸也不是各自独立。信息一重叠,维度就有压缩的空间,这正是主成分分析能起作用的前提。如果四列完全独立、方差相同,压完什么也剩不下。

2.3 数据进算法之前,先问三个问题

第一个问题,量纲一致吗。四列都是厘米,这次一致。但换成“身高加体重加收入”的组合,情况完全不同,方差大的那一列会控制主成分的方向,所以遇到不同性质的数据,标准化这一步躲不掉。

第二个问题,属性之间相关吗。相关意味着信息重叠,重叠意味着可以压缩。这一步判断决定了降维值不值得做、能做到什么程度。

第三个问题,有标签吗。鸢尾花数据是带答案的,每一行都知道自己属于哪一类。标签在不在,直接决定能用哪套方法:有标签可以上线性判别分析,没标签就只剩主成分分析和聚类。

这三个问题问完,后面的技术路线基本定了。很多人拿到数据直接调库,跑完发现结果不对劲,回头找原因,往往就是这三问里漏了一问。

3. 降维在降什么:主成分分析和线性判别分析的两条路子

3.1 四个维度画不出图,这是降维最原始的动机

人眼能看的是二维、三维。四维数据画不出来,画不出来就没法直观判断数据长什么样、分得开不开。这只是表象,真正的问题是计算量和噪声。

维度一高,计算量跟着涨;更麻烦的是噪声。四个属性里,有些方向的波动反映品种差异,有些方向的波动只是测量时的抖动。把这些方向同等对待,分类器就会被抖动带偏。降维做的事,就是给数据换一套坐标系,让有用的方向排在前面,没用的方向排在后面,然后掐掉后面那截。

打个比方,一群人站在一起拍合照。正面拍,大家高矮胖瘦挤成一团,看不出差别;换个侧面角度,谁肩宽、谁肚子大,一眼就分出来了。降维就是在找那个“侧面角度”。找法不同,就有了主成分分析和线性判别分析这两条路子。

3.2 主成分分析:不看答案,只找方差最大的方向

主成分分析的做法是纯几何的。它做一次正交变换,把一组可能相关的变量,转成一组线性不相关的新变量,新变量叫主成分。正交变换的好处是不改变点与点之间的距离关系,只是换了个角度看同一堆点。

那哪个角度好?用方差衡量。方差大,说明数据在这个方向上铺得开、区分度大;方差小,说明大家都挤在一块,信息量少。第一主成分是方差最大的方向,第二主成分是在与第一主成分垂直的所有方向里方差最大的那个,依此类推。

计算上落到协方差矩阵的特征值分解:最大特征值对应的单位特征向量,就是第一主成分的方向;第二大特征值对应的特征向量,就是第二个方向。想降到几维,就取前几个特征向量。

这里有个细节容易出问题:不同性质的数据量纲不一样,方差自然不一样。花萼长度用厘米,换成毫米,数值放大十倍,方差放大一百倍,主成分方向立刻跑偏。解决办法是先标准化,或者干脆用相关矩阵代替协方差矩阵,量纲影响就消掉了。这一步不是可选项。

代码实现上,一个最简版的拟合过程大概是这样:

import numpy as np def pca_fit(X, n_components): # X: 每行一个样本,每列一个属性 # 先标准化,再用相关矩阵,消掉量纲差异 X_std = (X - X.mean(axis=0)) / X.std(axis=0, ddof=1) R = np.corrcoef(X_std, rowvar=False) # 相关矩阵,对角线为 1 eigvals, eigvecs = np.linalg.eigh(R) # 对称矩阵用 eigh,结果更稳 order = np.argsort(eigvals)[::-1] # 特征值从大到小排 return eigvecs[:, order[:n_components]] # 取前 n 个方向 # 降维后的坐标 = X_std @ pca_fit(X, 2)

ddof=1表示按样本标准差计算,和统计课上的定义对齐;rowvar=False告诉函数每一列是一个变量,不然它会把每一行当成变量;eigh专门处理对称矩阵,比通用的eig数值上更稳;argsort默认升序,[::-1]翻转成降序。返回的是一组投影方向,把标准化后的数据乘上去,就得到降维结果。

主成分分析有个天生的局限:它不看标签。它只关心数据铺得开不开,不关心铺开之后类与类分不分得开。方差最大的方向,完全可能是两类混在一起的方向。

3.3 线性判别分析:带着答案去找方向

线性判别分析换了个目标。它知道每个样本属于哪一类,于是要找的方向不是“方差最大”,而是“同类挤得紧、异类离得远”。

通俗地说,把样本全部投影到一条直线上,好的投影应该让同一类的点聚成一小团,不同类的几团之间拉开距离。用统计的语言,就是让类间离差尽量大、类内离差尽量小,两者的比值构成一个目标函数,求它的最大值就得到投影方向。这个问题的解,落在类间散度矩阵与类内散度矩阵的广义特征向量上。

这跟主成分分析形成鲜明对照。主成分分析是无监督的,标签放在那儿也不用;线性判别分析是有监督的,标签是它全部的出发点。多用了这份信息,在有标签的场景下,它通常能比主成分分析降得更准——降维之后剩下的维度,恰好是区分类别最有用的维度。

代价也有。线性判别分析能保留的维度受类别数限制,一般不超过类别数减一。三个类别,最多降到二维。想要更多维度,它给不了。这也是它在高类别数场景下不如主成分分析灵活的原因。

3.4 一张表看清 PCA 和 LDA 的分野

对比项主成分分析(PCA)线性判别分析(LDA)
是否使用标签不使用,无监督使用,有监督
优化目标投影后方差最大类间离差大、类内离差小
降维维度上限受原始维度限制一般不超过类别数减一
适合的场景探索性分析、去噪、可视化已知类别、以分类为目的的降维
鸢尾花上的表现能压到二维看清大致结构同样压到二维,类间分离通常更明显

这两条路子没有绝对的高下。手上没有标签,或者想先看看数据长什么样,主成分分析更合适;标签齐全、目的就是分类,线性判别分析更划算。这份大作业的流程是两条都跑,然后拿刀切法比一比——这个思路比拍脑袋选一条靠谱得多,也能顺带看出“多用了标签”到底值多少。

4. 从距离到分布:马氏距离和高斯混合模型怎么配合

4.1 欧氏距离不够用:马氏距离在修正什么

两点之间直线最短,这是欧氏距离。它在平面上很好用,放到多维数据上会出问题。

第一个问题是量纲。判断两个人身材差异,只看体重差,矮个子会被冤枉;身高按厘米、体重按公斤,数值尺度差一个量级,算出来的距离基本被体重的差值主导。

第二个问题是相关性。身高和体重高度相关,两个指标其实在说同一件事,直接算距离等于把同一份差异算了两次。

马氏距离把这两点一起修正了。它的做法是把协方差矩阵拉进来:先用协方差把数据“掰”到各方向方差相同、彼此不相关的坐标系里,再量直线距离。通俗点说,它先把尺子校准,再量。

三个性质值得记住。第一个,当协方差矩阵是单位矩阵,也就是各指标互相独立、方差相同时,马氏距离退化成欧氏距离——欧氏距离是它的特例。第二个,它等价于把数据标准化之后算的欧氏距离。第三个,它是无量纲的,不受测量单位变化影响。这三点合起来,就是它在多维数据上比欧氏距离更讲道理的原因。

4.2 高斯混合模型:一团数据可能是几个钟形叠出来的

一个高斯分布,形状是钟形:一个中心也就是均值,一个胖瘦也就是方差。用它可以描述一堆围绕某个中心随机散布的点。

麻烦在于,现实里的“一类”往往不是一个中心。同一个品种的花,来自两个产地,尺寸整体平移了一点,数据就成了两团。这时用一个钟形去套,中间会鼓出一块不该有的部分。高斯混合模型的办法是:用若干个高斯成分按不同权重叠起来,去逼近真实分布。

每个成分有自己的均值、协方差和权重。参数怎么定?用极大似然估计——找一组参数,让“在这组参数下观测到现有样本”的概率最大。这个目标没有现成的公式解,实际计算靠迭代逼近,一轮一轮调参数,直到似然值不再明显上升。原料的数据处理部分就是在这套框架下,对降维后的数据估出每个成分的参数。

协方差矩阵在这里是主角。它不只记录每个维度各自的方差,还记录不同维度之间的相关性。花瓣长度和花瓣宽度一起变大变小,这个“一起”就藏在协方差矩阵的非对角线上。

4.3 和 k-均值比,它多考虑了哪些东西

聚类里最朴素的方法是 k-均值:先随便定几个中心,把每个点归到最近的中心,再重新算中心,反复几轮。它快、直观,但隐含了一个很强的假设——每个簇是以中心为球心、各方向等方差的球。

高斯混合模型把这个假设松开了。每个成分的协方差矩阵可以不一样,意味着它可以是扁的、长的、斜着放的椭球;再加上非对角线上的相关性,它还能歪着长。“考虑了不同方向上方差的不同,也考虑了不同维度之间的相关性”,说的就是这件事。

另一个差别是归属方式。k-均值是硬划分,一个点要么属于这簇,要么属于那簇;高斯混合模型给的是概率,一个点有七成可能属于第一类,三成属于第二类。概率化的归属在边界附近特别有用——那些模棱两可的点,本来就不该被硬塞进某一类。

4.4 三者串起来:降维、建模、判类

整条流程的顺序是:先用主成分分析或线性判别分析把四维压到低维,这同时也是去噪;再在低维数据上用高斯混合模型拟合,极大似然估出每个成分的均值和协方差;最后来了一个新样本,算它到各个成分的马氏距离,归到距离最小的那一类。

判类为什么用马氏距离而不是欧氏距离?因为每个成分的协方差不同,有的方向松、有的方向紧。马氏距离正好把这个形状吃进去,在一个方向上宽容、在另一个方向上严格。这正是它比欧氏距离更适合这里的理由。

import numpy as np def mahalanobis(x, mu, cov): # 协方差矩阵可能接近奇异,加一点对角扰动防止求逆失败 cov = cov + 1e-6 * np.eye(cov.shape[0]) diff = x - mu return float(np.sqrt(diff.T @ np.linalg.inv(cov) @ diff)) # 对每个高斯成分算一遍,取距离最小的成分所属的类 # scores = [mahalanobis(x, mu_k, cov_k) for mu_k, cov_k in components] # label = int(np.argmin(scores))

cov是某个成分的协方差矩阵,mu是它的均值向量,x是待判样本。1e-6 * np.eye(...)这一项是数值上的保险,协方差矩阵在样本少或者维度高的时候容易接近奇异,直接求逆会报错或者算出离谱的值。算出来的距离越小,说明这个样本在统计意义上越贴近这个成分。

到这里整条链路闭合了:降维解决维度太高看不过来的问题,高斯混合模型解决一类的形状不是一个球的问题,马氏距离解决怎么在形状不规则的空间里量远近的问题。三件事各管一段,谁也替代不了谁。

5. 常见误区辨析:这五个地方最容易想歪

5.1 降维这件事上的三个错觉

第一个易混点,把降维和特征选择当成一回事。两者都让列数变少,结果都是一张更窄的表,不写代码看不出区别,所以经常被混着说。区分的方法看留下的列是什么:特征选择是从原有列里挑,留下的还是花萼长度、花瓣宽度这些能读懂的原始量;主成分分析是造新列,新列是原来四列的线性组合,通常没法用一句“这是花萼长度”解释清楚。要可解释性,选特征选择;要压缩和去噪,主成分分析更合适。

第二个易混点,以为方差小的方向一定是噪声。主成分分析按方差排序,很自然会让人觉得排在后面就不重要,这个印象很顽固。但方差小只说明数据在这个方向上变化小,不说明它对分类没用。如果两类样本的均值差恰好落在一个方差很小的方向上,这个方向被丢掉,分类效果直接崩。稳妥的做法是把降维程度当成一个参数,用刀切法这类方式去试,而不是拍脑袋定一个维度。

第三个易混点,不做标准化也能直接做主成分分析。鸢尾花四列都是厘米,量纲一致,不做标准化好像也没出问题,于是形成了习惯。量纲一致只是特例。一旦混入不同单位的列,方差大的列会独占第一主成分。判断方法很简单——看有没有哪一列的数值范围明显大于其他列,有就先标准化,或者直接用相关矩阵。

5.2 模型和评估上的两个错觉

第四个易混点,把聚类和分类当成一回事。两者都在回答“这个样本属于哪一组”,输出形式也像,所以经常被等同。区别在标签:分类是有标签的,模型学的是什么样的特征对应什么标签;聚类没有标签,模型学的是数据自然聚成几团。高斯混合模型本身是聚类工具,这里之所以能用来判类,是因为先用了带标签的数据把每个类的分布拟合出来,再拿马氏距离去比——监督信息是外部加进去的,不是模型自己学到的。标签一旦没有,这套流程就得改成纯聚类,评估方式也要跟着换。

第五个易混点,把刀切法理解成“把数据分成两堆”的花哨说法。两者都涉及留一部分出来检验,所以容易混。普通划分是一次性的,切一刀、训练一次、测试一次;刀切法是每个样本都当过一次测试集——150 个样本就要训练 150 次,每次只留一个。代价是计算量,收益是每个样本都参与了检验,结论对划分方式不敏感。样本量大到几万条时,这个计算量会变得不现实,这时候才轮到 k 折交叉验证上场。

6. 什么时候用、什么时候别用:一份判断清单

这套流程不是万能的。它有一组隐含前提,满足了跑起来很顺,不满足就容易出现各种解释不清的问题。下面这张表是我自己踩过几次坑之后整理的判断依据。

你手上的情况更合适的做法
样本几十到几百条,四五个数值特征全套走一遍:降维、建模、刀切法评估
样本几万条以上留一法换成 k 折,训练次数扛不住
标签齐全,目的就是分类优先线性判别分析,主成分分析当对照
没有标签,只想看结构主成分分析加高斯混合模型,别硬套判别
特征严重偏态、多峰很尖先做变换,或者换非参数方法

有几条边界值得单独说。样本量小的时候,留一法几乎是唯一能榨出信息的选择,因为它不浪费任何一个样本;但样本量一大,它的代价就变成 n 次训练,无论如何都撑不住。类别数多的时候,线性判别分析能保留的维度会迅速撞上天花板,这时候主成分分析的灵活性反而更值钱。数据分布严重偏离钟形的时候,高斯混合模型给出的似然值就没有太大意义,硬用它做判类,结论只是数字好看。

我第一次把这套流程跑通的时候,觉得每个环节都理所当然,直到把降维维度从二维改成一维,判错率一下子跳上去,才发现前面那个“二维够了”的判断纯属拍脑袋。从那以后,我每次定降维维度,都强制走一遍刀切法,把不同维度和不同降维方法的判错率并排列出来再选,哪怕多花几分钟。这份大作业里把两种降维方法在不同维度下的表现逐一比对的思路,比任何一个单独的结论都更值得记住。希望帮到你。

本文还有配套的精品资源,点击获取