简介这是一份以北师大版《数理统计》课程为背景的大作业报告面向数理统计或机器学习初学者完整演示了如何对经典鸢尾花数据集进行统计建模与分析。报告应用了马氏距离、混合高斯模型、主成分分析、线性判别分析和刀切法等核心方法覆盖从数据预处理、降维、聚类到模型评估的完整链路。资源包共包含1个docx文档整体约511KB文档含摘要、符号说明、算法原理分析、数据处理分析和刀切法算法比对等章节层次清晰可直接作为课程设计参考。目前已有1426人学习下载。读者可从中学习如何利用马氏距离度量样本相似性借助主成分分析与线性判别分析进行降维和分类并通过高斯混合模型完成聚类最后用刀切法评估降维效果与模型差异对撰写同类统计大作业具有较好的借鉴价值。1. 数理统计大作业选鸢尾花一条能完整复用的分析链路数理统计大作业拿鸢尾花数据集开刀看起来是老生常谈但真要把主成分分析、线性判别分析、GMM、马氏距离和刀切法串成一条完整链路翻车点比想象中多。我最初以为跑几个现成函数就算完结果交上去被打回两次一次是标准化没做主成分被花萼长度单维带偏另一次是刀切法和 GMM 的协方差估计互相矛盾评估结果完全失真。这篇就把我改了两版之后的完整写法讲清楚包括符号说明怎么组织、PCA 和 LDA 的代码与差异、GMM 如何用马氏距离变成分类器以及三个最常见的坑。适合正在写大作业、或者想把这套降维密度估计留一验证流程复用到其他数据集的人。2. 动笔前先把骨架搭好符号、数据与算法原理的组织方式一份大作业能不能改出高分往往在动笔前就定了。我见过太多报告从「数据介绍」直接跳到「结果分析」中间的符号说明和算法原理全是抄书公式一大片但没人知道哪个符号对应哪列数据。这一章讲我怎么搭骨架。2.1 符号说明表先把约定写清楚再写公式我一般会这么比符号说明表不是凑篇幅而是整份报告的地图。先定义一套与后续代码一致的符号后面所有公式直接用这套符号评审顺着公式找代码时不会晕。符号含义x鸢尾花输入向量4 维花萼长、花萼宽、花瓣长、花瓣宽n样本个数150D样本数据矩阵150×4y类别标签0/1/2 对应三个品种μ均值向量Σ协方差矩阵R相关矩阵u线性变换方向主成分或判别方向这里有一个值得注意的细节Σ 在 PCA 阶段是 4×4 矩阵降维到 2 维后参与马氏距离计算时变成 2×2 矩阵符号含义随流程变化。我在自己的报告里专门加了一行注释「Σ 的维度与降维后数据维度一致」避免公式里出现维度对不上的尴尬。标签 0、1、2 的顺序也不要随便写我一般固定为 Setosa0、Versicolour1、Virginica2和原数据集默认顺序保持一致。后面画混淆矩阵、算每类错误数时这个固定顺序能替你省掉一堆查 bug 的时间。2.2 数据整理150 个样本、4 个属性、3 类标签怎么看数据从公开的机器学习数据库下载下来就是 150 行、5 列前 4 列是数值属性第 5 列是类别字符串。我建议在预处理阶段做两件事第一把类别字符串映射成 0/1/2 整数第二立刻做一个分组统计确认每类都是 50 个样本、每个属性没有缺失值。import pandas as pd df pd.read_csv(iris.csv) df.columns [sepal_len, sepal_wid, petal_len, petal_wid, label] df[y] df[label].astype(category).cat.codes print(df.groupby(y).describe())这段代码跑完后你应该看到三组的 4 个属性 min/max/mean 分布。这一步的价值在于它帮你预先判断算法可行性。比如花瓣长度的均值在三类之间差异非常明显而花萼宽度的重叠程度最高——这类先验直觉后期会反复用到尤其是做错误分析时。不要急着跳过这一步直接建模150 个样本的大作业虽然数据干净但亲手检查一遍分布心里才有底后面 GMM 要假设每个类别由正态成分生成如果某个属性强偏态或者有明显离群点这个假设就要打问号。2.3 算法原理章节怎么写围绕流程主线而不是抄定义我写算法原理这一章的原则是每个算法只交代「假设、输入、输出、在本作业里承担的角色」不抄教材定义。整份作业的分析主线是先降维去噪再假设每个类别服从一个正态成分用极大似然估计参数最后用马氏距离判类、用刀切法评估。算法承担角色关键假设PCA无监督降维去除属性间的线性相关方差代表信息量LDA有监督降维放大类间差异同类协方差相近GMM密度估计与聚类每个类别由一个正态成分生成马氏距离分类判定样本到类中心的标准化距离刀切法模型评估留一评估能近似泛化误差很多同学把原理章节写成「正态分布历史回顾」这就走偏了。评审想看到的不是你不知道正态分布的历史而是你怎么用这套工具解决一个具体的分类问题。所以每个算法的正文里我都只写四个点模型假设是什么、优化目标是什么、解法关键步是什么、在本作业的哪个环节被调用。按这个模板写原理章节想写飘都难。3. PCA 与 LDA 实战降维效果差在哪、怎么选这一章直接给能抄作业的核心代码和判断依据。先说结论在鸢尾花上PCA 降到 2 维能保留大部分方差但线性可分性不如 LDA 降到 2 维LDA 甚至可以降到 1 维就能把 Setosa 类完全分离。下面拆开讲。3.1 PCA 实现从相关矩阵到主成分方向的完整代码我这里不调现成的 PCA 封装而是手写一个精简版因为大作业里写清楚「相关矩阵 → 特征值 → 特征向量」这条链路比一行封装调用更能拿过程分。遇到追问实现细节的场合手写版的优势会非常明显。import numpy as np from numpy.linalg import eig class PCA: def __init__(self, n_components): self.n_components n_components self.components_ None def fit(self, X): # 标准化减去均值后除以标准差避免量纲影响 X_std (X - X.mean(axis0)) / X.std(axis0) R np.corrcoef(X_std.T) # 相关矩阵等价于标准化后的协方差矩阵 eigenvals, eigenvecs eig(R) # 按特征值降序排列取前 n_components 个特征向量 idx np.argsort(eigenvals)[::-1][:self.n_components] self.components_ eigenvecs[:, idx] return self def transform(self, X): X_std (X - X.mean(axis0)) / X.std(axis0) return X_std self.components_要点是np.corrcoef(X_std.T)这一步既消了量纲又让后续特征值约等于「每个主成分解释的方差比例」。对鸢尾花数据前两个特征值累计占比通常在 95% 以上这说明 4 维属性存在强线性相关降到 2 维损失的信息很少。如果你想把这个结论写进报告可以顺手打印一下特征值占比proportion eigenvals[idx][:self.n_components] / eigenvals.sum() print(解释方差占比:, proportion)如果不用标准化直接算协方差矩阵花萼长度数值 4.3~7.9会因为数值范围大而主导第一主成分散点图看着方差很大类别却挤成一团——这就是我开头说的第一次翻车。解释方差高不代表可分性好这个反直觉点放到答辩里反而是加分项。3.2 LDA 实现用标签信息找判别方向LDA 的思想是找一组方向 u让类间散度与类内散度的比值最大。对多类问题解法是求类内散度矩阵的逆乘以类间散度矩阵的特征向量。我一般会把公式拆成「类内小、类间大」这两句话写在报告里比抄一段教材推导更容易让评审跟住你的思路。class LDA: def __init__(self, n_components): self.n_components n_components self.components_ None def fit(self, X, y): classes np.unique(y) # 类内散度矩阵每个类内部离差之和 Sw np.zeros((X.shape[1], X.shape[1])) for c in classes: Xc X[y c] Sw (Xc - Xc.mean(axis0)).T (Xc - Xc.mean(axis0)) # 类间散度矩阵类中心到全局中心的加权离差 Sb np.zeros((X.shape[1], X.shape[1])) mu_all X.mean(axis0) for c in classes: Xc X[y c] mu_c Xc.mean(axis0) Sb len(Xc) * (mu_c - mu_all).reshape(-1, 1) (mu_c - mu_all).reshape(1, -1) # 求解广义特征值问题 eigenvals, eigenvecs np.linalg.eig(np.linalg.inv(Sw) Sb) idx np.argsort(eigenvals.real)[::-1][:self.n_components] self.components_ eigenvecs[:, idx].real return self注意一个硬限制对 c 类问题类间散度矩阵的秩最多是 c-1所以 LDA 最多给出 c-1 个非零特征值。鸢尾花有 3 类因此 LDA 最多降到 2 维。如果你强行取 3 个方向第三个方向基本是数值噪声GMM 拟合时还会遇到协方差矩阵接近奇异的报错。这个限制我在后面避坑章会再展开。3.3 PCA 与 LDA 对比四个维度看差异我把两个方法在鸢尾花上的表现整理成一张表答辩被问「为什么用 LDA」时全靠它撑场子对比维度PCALDA是否使用标签否是优化目标投影后方差最大类间离差/类内离差最大最大有效维度数4原始维度2类别数减一降 2 维后 Setosa 可分性很好很好降 2 维后 Versicolour/Virginica 可分性部分重叠明显分离计算依赖相关矩阵特征分解Sw⁻¹Sb 特征分解实际跑出来的结果也符合理论判断LDA 降 2 维后的散点图上三个类呈扇形分开只有 Versicolour 和 Virginica 边界有小块重叠PCA 降 2 维后这两个类的投影点互相穿插重叠区域明显更大。如果只降 1 维PCA 会把 Versicolour 和 Virginica 几乎压成同一条线LDA 则只是让两个类的投影区间有一点交叠。这里的选择逻辑很简单有标签的作业场景优先用 LDAPCA 更适合无标签或者想先看全局方差结构的场景。4. GMM 与马氏距离密度估计如何变成分类器降维不是目的分类才是。我采用的做法是对降维后的数据为每个类别拟合一个正态分布成分得到均值 μ_k 和协方差矩阵 Σ_k对新样本计算它到三个类中心的马氏距离距离最小者即预测类别。4.1 GMM 参数估计极大似然在降维数据上怎么落地这里要注意大作业场景下类别标签已知所以不需要像无监督聚类那样用 EM 迭代。直接按标签分组对每个组算样本均值和样本协方差就是极大似然意义下的参数估计def fit_gmm_by_class(X, y): params {} for c in np.unique(y): Xc X[y c] params[c] { mean: Xc.mean(axis0), cov: np.cov(Xc.T, biasTrue) } return paramsnp.cov(Xc.T, biasTrue)用有偏估计对应极大似然的协方差公式biasFalse是无偏估计两者在样本量只有 50 的时候有细微差别但不会影响这份作业的分类结论。代码注释里写清楚用了哪一种评审对细节的印象分会比预期高。GMM 的「混合」体现在不同正态成分的加权组合上但如果每个类正好对应一个成分权重就是各类先验概率。在鸢尾花上三类各 50 个样本先验都是 1/3所以真正起作用的是每个成分的均值和协方差权重只是均匀常数。4.2 马氏距离分类协方差如何参与距离计算马氏距离的公式是 (x-μ)ᵀΣ⁻¹(x-μ)它和欧氏距离的核心差别在于先按每个维度的方差做标准化再通过协方差矩阵消除维度之间的相关性。当 Σ 是单位阵时马氏距离退化为欧氏距离——理解了这一句话后面所有关于距离度量的选择题都能答。def mahalanobis_distance(x, mean, cov): diff x - mean inv_cov np.linalg.inv(cov) return np.sqrt(diff inv_cov diff) def predict(X, params): preds [] for x in X: dists [mahalanobis_distance(x, params[c][mean], params[c][cov]) for c in params] preds.append(np.argmin(dists)) return np.array(preds)为什么不用欧氏距离因为花瓣长度和花萼宽度的数值范围差别大欧氏距离会把数值大的花瓣长度当成主导项等于是隐性地给每个属性赋了不平等的权重。马氏距离天然做了标准化距离计算和量纲无关。还有一个值得写进报告的细节每个类应该有各自的协方差矩阵而不是共用一个。因为三个品种在花瓣属性上的散布范围明显不同共用协方差会把这类形状差异抹掉分类边界就会偏向方差大的类。4.3 结果判读错误样本集中在哪里、为什么把 PCA/LDA 降维后的数据分别喂给 GMM马氏距离我得到的错误分布非常有规律Setosa 类 0 错误Versicolour 和 Virginica 之间有少量互相误判。原因在数据分布上就能看出来——Setosa 的花瓣长度和花瓣宽度与另外两类完全不重叠而 Versicolour 与 Virginica 的花瓣属性存在交叠区域任何线性分类器在这个区域都会犯错。这一章的判断依据是不要只看总准确率要看误判分布。当错误全部集中在两个相邻类时说明模型本身没问题是数据固有重叠如果错误均匀分布在所有类那反而要怀疑标准化、降维或距离公式写错了。我第二次被打回就是因为只写了总准确率没写误判分布模型被当成黑匣子。之后我把混淆矩阵和每类的误判数补进报告问题立刻清楚了。5. 刀切法评估与常见问题四个坑让我改了两版作业刀切法留一法是这份大作业的收尾评估每次留 1 个样本当测试其余全部训练循环 150 次统计总错误率。它比随机划分的交叉验证更稳定尤其适合样本量只有 150 的小数据集。下面先给标准步骤再写我实际踩过的坑。5.1 刀切法步骤留一评估的正确打开方式从总体的训练样本中剔除一个用剩余样本建立判别规则用该规则对被剔除样本判别重复直到每个样本都被判过一次记错判数为 k。对每个类别都做同样操作最后错误率 总错判数 / 150。写成代码如下err_count 0 for i in range(n): train_idx np.arange(n) ! i # 布尔掩码排除第 i 个 X_train, y_train X[train_idx], y[train_idx] params fit_gmm_by_class(X_train, y_train) y_pred predict(X[i].reshape(1, -1), params)[0] if y_pred ! y[i]: err_count 1 accuracy 1 - err_count / n这里最关键的写法是np.arange(n) ! i它同时完成「留一」和「索引对齐」训练集是除了第 i 个以外的全部样本测试样本仍然用原始位置的第 i 个。如果先删掉第 i 行再取第 i 个取到的就是原第 i1 个样本评估结果直接失真。5.2 常见问题一标准化翻车主成分被单维属性带偏现象PCA 第一主成分解释方差超过 90%但降维后的散点图上三个类几乎粘在一起之后接 GMM 分类的准确率反而很低只有七成出头。原因没做标准化直接用原始协方差矩阵做特征分解。花萼长度的数值范围和方差明显大于花瓣宽度特征分解会把最高权重给方差大的维度第一主成分事实上变成「花萼长度单属性的投影」没有起到综合多属性的作用。解释方差高是假象可分性差才是真相。解决先对每个属性标准化减均值除标准差再用相关矩阵代替协方差矩阵。我在代码里用np.corrcoef(X_std.T)一步到位。事后检查特征值前两个主成分的解释方差之和超过 95%散点图也恢复了类间分离。这条教训之后我每次做 PCA 都会先打印特征值占比确认没有出现「单特征值独占」的异常。5.3 常见问题二协方差矩阵奇异GMM 直接报错现象降到 1 维或 2 维后np.linalg.inv(cov)报Singular matrix或者马氏距离算出来是负数/NaN程序跑一半就断。原因降维后的数据在某个方向上没有方差。典型场景是 LDA 被强行取了 3 个方向而第三方向特征值接近 0另一个场景是某个类在降维空间里样本退化到低维流形上协方差矩阵不满秩。我在第一次实验里就因为想「多降几维看看效果」把 LDA 设成 3 个方向结果正好踩中。解决先打印特征值丢弃特征值小于 1e-6 的方向对协方差矩阵加一个小的正则项cov 1e-6 * np.eye(d)再求逆。正规项目里这叫岭正则化大作业里注释一句「为避免数值奇异加入小幅扰动」就够不需要展开推导。5.4 常见问题三刀切法索引偏移评估结果虚高现象刀切法跑出来的准确率比正常交叉验证高出一大截甚至接近 100%但手动抽查几个样本发现明显不该分对的样本也被分对了。原因删除一个样本后代码里用np.delete(X, i, axis0)循环后续索引整体前移测试样本和训练标签错位等于用「正确答案附近的样本」当测试评估结果虚高。这种错误不报异常比协方差奇异更隐蔽血压容易上来。解决用布尔掩码保留原始索引即np.arange(n) ! i永远用原始数据的第 i 行做测试。我建议把这个写法作为固定模板因为索引偏移是逻辑层错误公式推导再漂亮也救不回来。5.5 常见问题四只看总准确率不看误判分布现象报告写「PCAGMM 准确率 90%LDAGMM 准确率 95%」答辩老师一句「误判集中在哪两个类」直接答不上来。原因评估只统计了准确率没有输出混淆矩阵模型在哪些地方犯错的细节完全丢失。总准确率是一个被平均过的数字它把 Setosa 的 0 错误和 Versicolour 的几条误判混在一起看不到结构。解决固定标签顺序后输出 3×3 混淆矩阵或者轻量地统计每个类的误判数真实类别误判去向误判数Setosa (0)无0Versicolour (1)Virginica 方向5~6Virginica (2)Versicolour 方向3~4对鸢尾花来说结果大概率是 Setosa 全对、另外两类在边界上互换少量样本。把这张小表放进报告既是评估结论也是数据分布重叠的直接证据属于性价比最高的半页纸。这一章其实是整份作业的兜底原理写得再漂亮评估环节的四个坑任何一个没躲开结果都不可信。我改的第二版作业很大篇幅就花在把标准化、特征值检查、索引对齐和混淆矩阵这四个点补全上。6. 结论与一个让报告多拿分的展示技巧6.1 最终结论降维方法、降维程度与分类效果的对应关系把前面的实验整理成一张主表这份大作业的结论就全部落在上面降维方法降维维度GMM马氏距离准确率留一估计PCA1约 75%~80%PCA2约 93%~95%LDA1约 90% 上下LDA2约 95%~97%不降维4 维4约 95% 上下结论有三条。第一LDA 在相同降维程度下优于 PCA因为它用了标签信息放大的是类间差异而不是全局方差。第二降维到 2 维是在分类准确率和计算复杂度之间最划算的点降到 1 维丢信息太多保留 4 维虽然准确率不低但计算量和噪声都更大。第三错误集中在 Versicolour 与 Virginica 之间这是数据固有重叠导致的不是模型实现错误。这三条每一条都能从之前的表或代码里找到支撑评审追问时不会答空。6.2 一个展示技巧让评估结果一眼看懂最后的技巧是不要只放准确率数字而是把三样东西拼成一页。第一张是降维后样本的散点图用三种颜色标类别一眼看出 PCA 与 LDA 的可分性差异第二张是马氏距离矩阵的热力图直观显示每个测试样本到三个类中心的距离深浅误判恰好发生在两个距离差不多的边界样本上第三张是上面那张准确率对比表。三样并排摆在一起评审扫一眼就能形成「LDA 更优、2 维够用、误判来自数据重叠」三个印象远胜一大段文字描述。回过头看这份大作业真正让我学到的东西不是某个算法推导而是「每一步结论都要有可以用代码复现的证据」的做事方式。从那以后我每次做数据分析作业都强制走一遍「标准化 → 检查特征值 → 输出混淆矩阵 → 留一验证」的标准流程哪怕数据集换成完全陌生的领域这个框架也能兜住大部分低级错误。希望这些经验对你写数理统计大作业有实际帮助。本文还有配套的精品资源点击获取