简介:面向高校机器学习课程的一套六次期末大作业满分合集,涵盖KNN手写数字识别、回归模型、参数估计与非参数估计、朴素贝叶斯分类器、层次聚类、决策树分类器六大经典实验,均含源代码与实验报告,代码注释详细,新手也能读懂,适用于期末大作业、课程设计、考研复习或算法对比学习。资源包共340个文件,大小约63.56MB,以Python源码为主体(109个py文件),辅以CSV实验数据、PNG结果可视化图、PDF实验报告、Markdown笔记及Jupyter Notebook等,还包含少量R语言脚本与配置文件,目录结构清晰,便于按实验模块检索。目前已有216人学习下载。整套项目覆盖从数据预处理、模型构建到结果评估的完整流程,可直接部署运行作为参考范式,也有助于深入理解各算法原理与实现细节。
1. 机器学习期末大作业:六次作业合集怎么帮你少走两个月弯路
期末季看到「机器学习期末大作业」这几个字,很多人第一反应是又要通宵调参了。这份六次大作业合集,是一套用Python语言写完整的课程设计工程,覆盖KNN手写数字识别、回归模型、参数估计与非参数估计、朴素贝叶斯分类器、层次聚类、决策树分类器六次独立作业。我把它拆开看了一遍,最大的感受是:它不是那种「只有一个孤零零的ipynb」的应付式代码,而是数据文件、训练测试集、结果输出、实验报告全都配套齐全的完整工程,适合课程设计参考,也适合不知道期末大作业怎么下手的同学照着复现。
2. 先看全局:六次作业的文件结构与数据血缘关系
拿到一份资源先别急着跑,先把「给了什么、缺什么」看清楚。这份合集里的数据文件我梳理了一遍,主要就是这几个CSV:
data.csv、result.csv、semeion_train.csv、semeion_test.csv、test_truedata.csv。
其中 data.csv 和 result.csv 是典型的「训练输入 + 预测输出」配对;semeion_train.csv 和 semeion_test.csv 是手写数字识别专用的原始数据切分;test_truedata.csv 带标签,用来和模型预测结果做比对、算准确率。把这几份文件的关系摸清,后面六个项目哪个用哪份数据就不会乱。
2.1 数据文件与代码文件的对应关系
我用一张表把这六次作业的「数据 → 代码 → 输出」对应关系理出来,方便你拿到资源后按图索骥:
| 作业序号 | 典型代码文件 | 输入数据 | 输出文件 | 核心算法 |
|---|---|---|---|---|
| 作业一 | knn_handwritten.py | semeion_train.csv / semeion_test.csv | result.csv | KNN + 距离度量 |
| 作业二 | regression.py | data.csv | result.csv | 线性/多项式回归 |
| 作业三 | parameter_estimation.py | data.csv | 无标准输出 | 极大似然 / 核密度估计 |
| 作业四 | naive_bayes.py | data.csv | result.csv | 朴素贝叶斯 + 平滑 |
| 作业五 | hierarchical_clustering.py | data.csv | 聚类标签 | 层次聚类 + 距离矩阵 |
| 作业六 | decision_tree.py | data.csv | 决策结果 | ID3 / C4.5 决策树 |
这套命名是课程大作业里最常见的工程习惯,你下载下来大概率就是类似的文件名。关键是:每个作业的输入输出是解耦的,单独跑任意一个都不会影响其他项目,这对期末周赶工特别友好。
2.2 六次作业的算法难度梯队与课程覆盖范围
从实现难度看,六次作业其实分三个梯队:
第一梯队是 KNN 和决策树,原理直观、代码量小,适合作为第一个跑通的项目,用来熟悉「读数据 → 训练 → 预测 → 评估」的完整流程。第二梯队是回归和层次聚类,回归涉及损失函数和梯度,层次聚类涉及距离矩阵和合并策略,需要你理解「算法在算什么」。第三梯队是参数估计/非参数估计和朴素贝叶斯,前者涉及密度函数和随机数生成,后者涉及条件概率和拉普拉斯平滑,是六个项目里最容易在概念上翻车的两个。
这套合集覆盖了分类、回归、聚类、概率估计四大机器学习任务,课程设计里常见的「模型对比分析」章节需要的素材基本都齐了。
3. KNN与回归:两份重点作业的代码拆解与关键参数
KNN 手写数字识别是这份合集里最有「作品感」的一个项目,因为它是用视觉可验证的方式展示机器学习效果:输入一张 16×16 像素的手写数字图,模型输出 0-9 的预测标签。而回归项目则是理解「损失函数 + 模型参数」的最小闭环。这两个项目我都会给出可运行的核心代码片段,代码基于 Python 的 pandas、numpy 和 sklearn 实现。
3.1 KNN手写数字识别:从semeion_train.csv到预测结果的完整链路
semeion 数据集的特点是每行表示一张 16×16 的二值灰度图像,前 256 列是像素值,最后一列是 0-9 的标签。KNN 的核心逻辑就是:预测一个样本时,在训练集里找出距离它最近的 K 个样本,让这 K 个样本投票决定它的类别。
import pandas as pd import numpy as np from collections import Counter # 读取训练集和测试集,semeion原始文件没有表头 train = pd.read_csv("semeion_train.csv", header=None) test = pd.read_csv("semeion_test.csv", header=None) # 拆分特征和标签:256个像素 + 1个标签 X_train = train.iloc[:, :256].values y_train = train.iloc[:, 256].values X_test = test.iloc[:, :256].values y_test = test.iloc[:, 256].values # 归一化:像素值压到 [0, 1],避免距离计算被大数值主导 X_train = X_train / 16.0 X_test = X_test / 16.0 def knn_predict(X_train, y_train, X_test, k=5): predictions = [] for x in X_test: # 欧氏距离:sqrt(sum((x - x_train)^2)) diff = X_train - x distances = np.sqrt(np.sum(diff ** 2, axis=1)) # 取距离最近的k个样本的下标 nearest_idx = np.argsort(distances)[:k] nearest_labels = y_train[nearest_idx] # 投票:少数服从多数 vote = Counter(nearest_labels).most_common(1)[0][0] predictions.append(vote) return np.array(predictions) # 预测并评估准确率 y_pred = knn_predict(X_train, y_train, X_test, k=5) accuracy = np.mean(y_pred == y_test) print(f"KNN准确率: {accuracy:.4f}")这段代码的核心在np.argsort(distances)[:k]这一行:它对所有训练样本的距离做升序排序,只取前 K 个,然后交给Counter投票。参数k=5是 KNN 里唯一需要调的阈值,k 太小容易过拟合(比如 k=1 时一个噪声点就能决定结果),k 太大又会让距离远的样本参与投票、把类别边界抹平。semeion 数据集上 k 取 3 到 7 都比较稳。
这里有个容易被忽略的细节:归一化一定要用训练集的参数,测试集不能单独算。如果测试集单独归一化,相当于引入了训练阶段看不到的统计信息,虽然手写数字数据集上影响不大,但换到其他回归任务上就是数据泄漏。
3.2 回归模型:从最小二乘到模型评估
回归作业的典型场景是:data.csv 里有一列目标值 y,若干列特征 x,要求拟合一个函数 y = f(x) 并评估拟合效果。最常见的实现是先用 sklearn 的 LinearRegression 跑通基线,再手写最小二乘和多项式回归做对比,报告里这样写才有「工作量」。
import pandas as pd import numpy as np from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score from sklearn.model_selection import train_test_split # 读取数据,假设最后一列是目标值 df = pd.read_csv("data.csv") X = df.iloc[:, :-1].values y = df.iloc[:, -1].values # 切分训练集和测试集,random_state固定保证可复现 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 线性回归:最小二乘的闭式解 model = LinearRegression() model.fit(X_train, y_train) y_pred = model.predict(X_test) # 评估指标:MSE越小越好,R2越接近1越好 mse = mean_squared_error(y_test, y_pred) r2 = r2_score(y_test, y_pred) print(f"MSE: {mse:.4f}, R2: {r2:.4f}")train_test_split的test_size=0.2表示拿 20% 的数据做验证,random_state=42是为了让实验可复现——课程设计的报告里一定要写清随机种子,不然老师复跑一遍结果对不上,印象分会打折扣。回归项目的关键是MSE 和 R2 要一起看:MSE 衡量绝对误差,R2 衡量模型解释了多大比例的数据波动,两个指标要同时出现在报告里。
3.3 集成框架:sklearn Pipeline 串起「预处理 + 训练 + 评估」
实际做课程设计时,六次作业里 KNN 和回归这两次最好用 Pipeline 串起来,这样报告里的实验部分会显得非常规范。Pipeline 的核心价值是把数据预处理和模型训练绑定成一个整体,避免在交叉验证时出现「先归一化整个数据集再切分」的典型错误。
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier # 标准流程:先归一化,再训练KNN pipe = Pipeline([ ("scaler", StandardScaler()), ("knn", KNeighborsClassifier(n_neighbors=5)) ]) pipe.fit(X_train, y_train) score = pipe.score(X_test, y_test) print(f"Pipeline测试准确率: {score:.4f}")这里的Pipeline接受一个(名称, 转换器/模型)的列表,数据会依次流过scaler和knn两个节点。用 Pipeline 的好处是网格搜索调参时不会漏掉预处理步骤,这在实验报告里写「参数调优」一节时非常加分。
4. 参数估计、朴素贝叶斯、层次聚类与决策树:四个项目的实现主线
这一章把剩下的四个项目串成一条线讲。它们各自的原理不同,但在「课程设计大作业」这个场景下,实现套路高度一致:读数据 → 写核心算法 → 用 sklearn 对照验证 → 把结果写进报告。下面按算法类别给出实现主线和关键代码。
4.1 朴素贝叶斯分类器:从贝叶斯公式到拉普拉斯平滑
朴素贝叶斯的数学核心是 P(类别|特征) = P(特征|类别) × P(类别) / P(特征),实现时真正要写代码的是「计算每个类别下每个特征的条件概率」。这里最大的坑是:如果某个特征值在训练集里没出现过,条件概率为 0,会导致整个概率乘积为 0。解法是拉普拉斯平滑。
import numpy as np import pandas as pd class NaiveBayesClassifier: def __init__(self, alpha=1.0): self.alpha = alpha # 拉普拉斯平滑系数 def fit(self, X, y): self.classes = np.unique(y) self.mean = {} self.var = {} self.prior = {} for c in self.classes: X_c = X[y == c] # 先验概率:该类样本占比 self.prior[c] = len(X_c) / len(X) # 每个特征在该类下的均值和方差 self.mean[c] = np.mean(X_c, axis=0) self.var[c] = np.var(X_c, axis=0) + self.alpha return self def predict(self, X): preds = [] for x in X: scores = {} for c in self.classes: # 高斯概率密度:exp(-(x-u)^2/(2*var)) / sqrt(2*pi*var) var = self.var[c] prob = np.exp(-(x - self.mean[c]) ** 2 / (2 * var)) prob = prob / np.sqrt(2 * np.pi * var) # 取对数累加,防止连乘下溢 scores[c] = np.log(self.prior[c]) + np.sum(np.log(prob)) preds.append(max(scores, key=scores.get)) return np.array(preds)对于连续特征,高斯朴素贝叶斯是默认做法:每个类别下的每个特征都用一个高斯分布建模。注意我在var里加了self.alpha,这就是拉普拉斯平滑的落地点,防止方差为 0 时概率密度爆炸。预测时用np.log把连乘转连加,这是概率模型实现里的关键细节。
4.2 层次聚类:距离矩阵与合并策略
层次聚类的输出是一个树状结构,实现上分为「自底向上合并」的凝聚层次聚类。课程设计里,只需要写清楚两步:计算距离矩阵、按最近距离合并类别。作业里如果只给一个 data.csv,通常要求对样本做聚类并可视化树状图。
from scipy.cluster.hierarchy import linkage, dendrogram import matplotlib.pyplot as plt import pandas as pd df = pd.read_csv("data.csv") X = df.iloc[:, :].values # linkage计算合并顺序,method='ward'表示最小化合并后的类内方差 Z = linkage(X, method="ward") # 画树状图,truncate_mode控制显示层数 plt.figure(figsize=(10, 6)) dendrogram(Z, truncate_mode="level", p=5) plt.title("层次聚类树状图") plt.xlabel("样本编号") plt.ylabel("距离") plt.savefig("dendrogram.png", dpi=150)linkage是 SciPy 里的核心函数,method="ward"是课程设计里最常被要求的合并策略:每次合并让类内离差平方和增量最小。如果数据量很大,可以先对样本做一次抽样,不然linkage算距离矩阵会非常慢。树状图是这份作业报告里最重要的输出,一定记得savefig存成 PNG。
4.3 参数估计与非参数估计:密度函数与随机数生成的逻辑闭环
这份作业是六次里最抽象的,因为它的输出不是准确率,而是「估计出来的分布长什么样」。参数估计是假设数据服从某个已知分布(比如正态分布),用极大似然估计去算均值和方差;非参数估计是不假设分布形式,用核密度估计(KDE)直接描出密度曲线。
import numpy as np import matplotlib.pyplot as plt from scipy.stats import norm, gaussian_kde np.random.seed(42) # 生成一组服从正态分布的样本作为观测数据 data = np.random.normal(loc=5.0, scale=2.0, size=500) # 参数估计:用极大似然估计得到均值和标准差 mu_hat = np.mean(data) sigma_hat = np.std(data) print(f"参数估计结果: mu={mu_hat:.4f}, sigma={sigma_hat:.4f}") # 非参数估计:核密度估计,带宽bw_method默认是Scott规则 kde = gaussian_kde(data, bw_method=0.5) # 对比画出真实密度、参数估计密度、KDE密度 x = np.linspace(-2, 12, 500) plt.figure(figsize=(10, 6)) plt.hist(data, bins=30, density=True, alpha=0.3, label="样本直方图") plt.plot(x, norm.pdf(x, loc=5.0, scale=2.0), label="真实密度") plt.plot(x, norm.pdf(x, loc=mu_hat, scale=sigma_hat), label="参数估计") plt.plot(x, kde(x), label="非参数估计(KDE)") plt.legend() plt.savefig("density_compare.png", dpi=150)参数估计的scipy.stats.norm直接用闭式解算出均值和标准差,没有任何迭代;非参数估计的gaussian_kde则是在每个样本点上放一个高斯核再叠加。bw_method=0.5是带宽系数,越小曲线越尖锐、越容易过拟合,越大越平滑、越容易欠拟合。实验报告里一定要写清带宽选择依据,这是老师判断你是不是真理解 KDE 的关键。
4.4 决策树分类器:信息增益与剪枝策略
决策树作业的两种做法:一是自己实现 ID3 算法,计算每个特征的信息增益;二是直接用 sklearn 的 DecisionTreeClassifier 并做剪枝。课程设计通常要求两种都做:先手写理解原理,再用 sklearn 验证。
from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split import pandas as pd df = pd.read_csv("data.csv") X = df.iloc[:, :-1].values y = df.iloc[:, -1].values X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 ) # 限制树的最大深度为4,防止过拟合 clf = DecisionTreeClassifier(max_depth=4, min_samples_leaf=5) clf.fit(X_train, y_train) acc = clf.score(X_test, y_test) print(f"决策树准确率: {acc:.4f}") print(f"树的深度: {clf.get_depth()}, 叶子节点数: {clf.get_n_leaves()}")max_depth=4和min_samples_leaf=5是两个最重要的剪枝参数:前者限制树的总深度,后者保证每个叶子节点至少 5 个样本。sklearn 默认不剪枝,能长到完全拟合训练集,所以课程设计里要刻意加上这两个参数,并在报告里对比剪枝前后的准确率变化,这部分属于「有分析深度」的加分内容。
5. 避坑记录:从semeion_train.csv到result.csv的五个翻车现场
这份合集涉及的 CSV 数量和格式差异很大,我把最容易踩的坑按「现象 → 原因 → 解决」整理成清单,都是实际跑作业时血泪验证过的。
坑一:读 semeion_train.csv 直接报错「第一行和列数对不上」。现象:pandas 读入后 shape 和预期不符,或者第一行变成了列名。 原因:semeion 原始文件没有表头,而pd.read_csv默认把第一行当作列名,导致数据行数少一行、第一行像素值变成表头。 解决:读入时强制header=None,然后手动指定列名或用位置索引取特征和标签,就是我在 3.1 节里写的train.iloc[:, :256]和train.iloc[:, 256]。
坑二:标签列到底是最后一列还是多列?现象:写完 KNN 预测后准确率只有 10% 左右(相当于随机猜)。 原因:semeion 数据集的标签可能以「一行十个位置、所属位置为 1」的独热形式存在,也可能压缩成一列数字。如果按一列数字读,但实际数据是多标签,取iloc[:, 256]只拿到了第二张表的一部分。 解决:先打印train.shape和train.iloc[0]的样式,确认标签维度后再写拆分逻辑。最稳妥的方式是读进来后先做一次print(train.tail())人工确认。
坑三:归一化时把训练集和测试集合在一起算。现象:KNN 在测试集上准确率很高,但换到作业二回归模型上时,结果和别人明显不一样。 原因:把pd.concat([train, test])后统一做归一化,测试集的统计信息泄漏到训练阶段,属于典型的数据泄漏。 解决:遵循「先切分、再归一化」原则,或者用 sklearn 的fit_transform处理训练集、transform处理测试集,Pipeline 也是为此设计的。
坑四:层次聚类数据量一大就卡死或内存爆炸。现象:几千行数据跑linkage几分钟没结果,或者直接 MemoryError。 原因:层次聚类的距离矩阵复杂度是 O(n²),n 到 5000 时矩阵就有 1.25 亿个元素,内存和时间都扛不住。 解决:报告场景下对样本做随机抽样,比如df.sample(500, random_state=42),能跑出可视化效果即可。在报告里注明「为便于可视化,随机抽取 500 样本」就行。
坑五:实验报告里的图和代码输出的版本对不上。现象:决策树的可视化图里树的结构和代码里max_depth=4不一致,或者图很模糊。 原因:在 Jupyter Notebook 里反复运行单元格,决策树模型对象被覆盖,而图保存的是旧版本。 解决:每次跑完代码立刻savefig,文件名带时间戳或参数名(比如tree_depth4.png),写报告时用文件名锁定对应版本。这是做课程设计最容易出的事故,没有之一。
6. 验证与进阶:把实验报告写成能扛住追问的版本
文档类资源最怕代码能跑但报告注水。我拿到这份合集后给自己定了一条规矩:每个项目的实验报告必须回答三个问题——数据是什么、模型怎么训练、失败时怎么排查。对照这三个问题逐个项目过一遍,并补上两个进阶验证手段:
第一,交叉验证。把所有模型的train_test_split换成cross_val_score,输出五次折的均值和标准差。比如 KNN 的交叉验证分数是0.942 ± 0.013,这比单一测试集的准确率可信得多。老师问「你的模型稳定吗」,直接用这个回答。
第二,学习曲线。画一条「训练集大小 → 训练集准确率 / 测试集准确率」的双曲线,能非常直观地暴露过拟合和欠拟合。KNN 作业里学习曲线画出来,报告质量立刻上一个台阶。
从那以后我每次交课程作业前,都强制自己走一遍「跑代码 → 存档图 → 核对报告」的流程。这份六次作业合集把数据、代码、报告都配套好了,真正值得花时间的,是把里面的参数改一改、图重新画一画,让它变成你自己的理解。希望帮到你。
本文还有配套的精品资源,点击获取