
简介这份机器学习期末复习题及答案面向高校期末备考学生与需要快速梳理知识框架的自学者围绕分类、回归、聚类、神经网络等核心考点组织题目与参考答案适合考前自测、查漏补缺和课堂复习对照。压缩包内仅含1个docx文件大小约20KB体积轻便便于打印或导入电子笔记。题目覆盖单项选择、多项选择、名词解释、简答与编程题既能练习K近邻、朴素贝叶斯、支持向量机、决策树等算法原理也能对照欠拟合、过拟合、线性回归、均方误差、数据清洗等概念的标准表述编程题还给出简单线性回归与K近邻分类的实现步骤。已有13141人学习说明其题目结构与答案组织具有一定参考价值适合作为期末冲刺阶段的成套练习材料。1. 一份期末题库为什么值得当速查表翻第二遍前阵子有人拿着两份拼在一起的机器学习期末复习题及答案来问说除了考前突击还能不能有别的用法。翻完之后发现这东西其实是一张压缩得相当厉害的知识索引单选多选把训练集与测试集划分、欠拟合与过拟合的处置方向、KNN 属于哪类学习、朴素贝叶斯判定式、SVM 核函数、决策树三种生成算法、MAE 与 RMSE 的区分这些每轮技术面都会被追问的点挨个过了一遍名词解释和简答给了可以直接拿来做口径对齐的表述编程题则把 sklearn 的调用顺序钉死成六步——导库、导数据、实例化、拟合、预测、评价。适合两类人期末要过线的和离开课堂两三年、需要一份能随时翻的口径对照表的从业者。它的价值不在题目本身而在于把散落的术语压成了可判定、可核对的一句话结论。2. 先立知识骨架监督口径与欠拟合、过拟合的判定边界这套题最容易被低估的地方是它前几道单选其实在考同一件事你能不能先判断这个模型现在处于什么状态。状态判断错了后面调参、换算法、加数据全是白费。所以不急着背答案先把骨架搭起来。2.1 训练集与测试集这条线画错后面全歪第一题问机器学习把数据集分成训练集和什么答案是测试集。这道题简单到容易被跳过但它对应的是整套评估体系的根基。模型在训练集上把参数拟合出来本质是在最小化训练损失而训练损失低从来不等于泛化误差低。用训练集去评价模型等于让考生自己出卷自己判分分数一定好看。测试集的作用就是提供一份模型从没见过的样本用它算出来的指标才具备外推意义。工程上常见的做法是三段切分训练集拟合参数验证集挑超参数k 值、树深、正则系数测试集只在最后报告一次。如果拿验证集反复调、调到满意为止那验证集其实已经被看过了此时它的分数会偏乐观。sklearn 里的train_test_split是最常用的切分入口几个参数值得记住test_size测试集占比回归类小数据集常用 0.20.3样本量低于 200 时再往上调会让测试集小到指标抖动剧烈random_state固定随机种子保证你和别人跑出来的是同一份划分复现问题时必设stratifyy分层抽样分类任务里类别不均衡时必加否则可能出现某个类别在测试集里一个样本都没有。from sklearn.model_selection import train_test_split import numpy as np X np.array([[3, 2], [3, 1], [1, 3], [3, 4], [2, 2], [7, 4], [5, 3], [9, 2], [7, 3], [7, 0]]) y np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]) # test_size0.3 留 3 个样本做测试stratifyy 保证两类的比例在两边一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) print(X_train.shape, X_test.shape) # (7, 2) (3, 2)这段代码的重点不在切分本身而在stratify那一行十行数据、两类各五个不分层的话随机切出测试集全是 1 类这种情况是完全可能的而一旦发生你算出来的准确率不是 0 就是 1毫无参考价值。样本量越小分层越不能省。提示切分之前不要做标准化、归一化或者任何用到全体数据统计量的预处理否则测试集的均值方差信息会泄漏进训练过程。正确顺序是先切分再在训练集上fit预处理器然后transform测试集。2.2 欠拟合那道题错的人比想象中多原题是以下属于解决模型欠拟合的方法是四个选项里给了增加训练数据量、对模型进行裁剪、增加训练过程的迭代次数、正则化标准答案取增加迭代次数。这道题答错多半不是没背而是没把欠拟合和过拟合的处置方向分清。判据只有一条看训练误差训练误差高、测试误差也高是欠拟合模型容量不够或者根本没训到位训练误差低、测试误差明显高是过拟合模型把噪声也记住了。两条路的方向完全相反状态训练误差测试误差处置方向具体手段欠拟合高高提升容量或延长训练加特征、加多项式项、加网络宽度/深度、增加迭代轮数、减小正则强度过拟合低高降低容量或加约束剪枝、降维、减少特征、L1/L2 正则、早停、Dropout、增加数据对着这张表看四个选项就清楚了增加训练数据量对欠拟合基本无效因为数据再多也补不上模型表达能力的缺口对模型裁剪是降低容量方向反了正则化是给损失加惩罚项压制复杂度那是治过拟合的只有增加迭代次数对梯度下降这类迭代式优化确实能让训练损失继续往下走。但这里有个必须说清的边界增加迭代次数只在模型本身容量够、只是没收敛时才成立。如果模型是线性回归去拟合一条明显弯曲的关系你把迭代次数加到十万次损失也就停在那个水平不动了因为假设空间里压根没有那条曲线。做树模型时这条更是完全不适用——决策树不是靠迭代逼近的max_depth、min_samples_leaf才是它的容量旋钮。2.3 算法谱系有监督、无监督与生成式、判别式的划分口径题库里反复出现的KNN 是哪种学习PCA 降维是哪种学习聚类属于什么考的都是同一个分类口径。很多人的混淆点在于把算法名字听起来像什么当成了判据实际上唯一的判据是训练时用没用到标签 y。算法主要任务学习类型关键超参数常见误用KNN分类/回归有监督n_neighbors、距离度量不做标准化直接喂量纲差异大的特征朴素贝叶斯分类有监督生成式alpha平滑系数把连续特征硬套多项式版本决策树分类/回归有监督criterion、max_depth不剪枝训练集 100% 就以为模型好逻辑回归分类有监督判别式C、penalty把输出当概率直接用于代价敏感场景线性回归 / 岭回归 / Lasso回归有监督alpha正则强度特征量纲差异大时不做标准化K-Means聚类无监督n_clusters、init簇数拍脑袋定不看肘部或轮廓系数PCA降维无监督n_components在切分之前对全体数据 fit造成泄漏神经网络分类/回归/翻译有监督为主层数、宽度、学习率、batch size学习率过大直接发散还以为是数据问题KNN 之所以是有监督在于它的预测过程需要邻居的标签来投票没有 y 它什么也输出不了PCA 之所以是无监督在于它只寻找数据方差最大的投影方向全程不碰标签聚类同理它按距离把样本分组组的意义需要人后验去解释。这三条是这套卷子里重复率最高的混淆点。再往下还有一层划分朴素贝叶斯是生成式它建模的是P(x|y)P(y)先学每个类别下特征的分布再用贝叶斯公式反推后验逻辑回归和 SVM 是判别式直接建模P(y|x)或者干脆只学一个决策边界。这个区别在实际工作中的体现是生成式在小样本下往往更稳判别式在样本充足时通常精度更高。3. 选择题的七个高频点从朴素贝叶斯公式到决策树划分标准单选多选加起来二十来道真正有信息量的其实集中在七个点上。这些点不是背完就完它们在后面对应的是具体的 API 参数和报错信息。3.1 朴素贝叶斯的判定式以及朴素两个字的代价题目给的判定式是H(x) argmax P(Ci) Π P(ak|Ci)。三个符号各有含义argmax取的是让后面那一坨最大的类别标号不是概率值本身写成max就直接丢掉了这是哪一类这个最关键的信息Π是连乘遍历样本的每一个特征P(Ci)是先验P(ak|Ci)是类别 Ci 下第 k 个特征取值为 ak 的条件概率。连乘从哪来的来自朴素这个假设——假定在给定类别的条件下各个特征相互独立。有了这个假设联合概率才能拆成一串条件概率的乘积。代价也很直接现实中特征往往相关比如身高和体重独立性假设一旦被破坏概率估计就会偏。但它依然好用因为分类任务只要求 argmax 的位置对不要求概率值绝对准确排序对了结果就对。工程落地时有两个必踩的点。一是连乘下溢特征一多几十个小于 1 的概率相乘浮点数直接变成 0所有类别都是 0argmax 就失效了。标准解法是取对数把连乘变成连加log P(Ci) Σ log P(ak|Ci)单调性不变结果等价。二是零概率如果某个特征取值在某个类别下从没出现过P(ak|Ci)0一乘整个类别就被判死刑这时候要靠拉普拉斯平滑也就是 sklearn 里的alpha参数。from sklearn.naive_bayes import MultinomialNB, GaussianNB # alpha1.0 是拉普拉斯平滑alpha 越小越贴近原始频率但零概率风险越高 mnb MultinomialNB(alpha1.0) # 适用于离散计数特征如词频 gnb GaussianNB() # 适用于连续特征假定每个类别内特征服从正态分布选哪个版本取决于特征类型这一点题库没考但工程上天天遇到文本词频、离散计数用MultinomialNB连续数值特征用GaussianNB它不去连乘离散概率而是用每个类别内特征的均值和方差算高斯密度特征全是 0/1 的用BernoulliNB。硬把连续值塞进多项式版本会直接报特征值必须非负的错。3.2 SVM 的核函数与线性可分、不可分的几何口径关于核函数的那道题四个选项里贝叶斯核函数网络核函数是纯凑数的干扰项真实存在的常用核函数是线性核、多项式核和高斯核RBF。高斯核之所以成为默认选择是因为它把样本映射到无穷维空间理论上能拟合任意复杂的边界代价是参数敏感、解释性差。线性可分与不可分的几何表述值得记牢简答题里就考了这个一维空间分开两个点集只需找一个点二维需要一条直线三维需要一个平面n 维需要的是一个超平面。找不到这样一个超平面把两类完全隔开就是线性不可分。这时候两条路——升维用核函数隐式映射到高维在高维里找超平面或者允许少量样本越界软间隔用松弛变量加惩罚。sklearn 的SVC里两个参数方向必须清楚from sklearn.svm import SVC # kernelrbf 高斯核C 是惩罚系数C 越大越不容忍越界边界越曲折 # gamma 控制单个样本的影响半径gamma 越大边界越贴合训练点 clf SVC(kernelrbf, C1.0, gammascale)C调大的效果和正则化强度调小是一回事训练集精度上去泛化风险也上去。gamma调大则相当于每个样本的势力范围缩小边界会绕着单个点打转是过拟合的典型信号。这两个参数一起调的时候容易互相掩盖问题实务上先固定gammascale扫C再回头微调gamma比网格一把梭更容易看懂发生了什么。3.3 决策树的三种生成算法与两套划分标准题目问决策树生成有几种算法答案取 3对应 ID3、C4.5、CART。选项里出现的 ID4 是不存在的这是典型的靠记忆混淆来设的坑把ID3 的下一版想当然推成 ID4而真实的演进路线是 ID3 → C4.5 → CART。三者真正的差别在划分标准上算法划分标准树形能否回归已知短板ID3信息增益多叉否偏向取值多的特征C4.5信息增益率多叉否增益率对取值少的特征又偏保守CART基尼指数二叉树是单棵树方差大需靠集成补信息增益的定义是划分前后信息熵的差值熵用-Σ p log p算增益越大说明这次划分让不确定性下降得越多。问题在于取值种类多的特征比如用户 ID天然能把数据切得很碎增益虚高所以 C4.5 引入增益率用特征自身的分裂信息量做分母来惩罚。CART 走的是另一条路用基尼指数1 - Σ p²衡量不纯度二分裂且天然支持回归用平方误差代替基尼。对应到代码就是DecisionTreeClassifier的criterion参数取gini或entropy前者就是 CART 的默认行为后者相当于借用信息增益的思路。做回归时换DecisionTreeRegressor此时criterion的取值变成squared_error这类误差口径不再是基尼。注意单棵决策树不做任何限制时一定会长到每个叶子只有一个样本训练集准确率 100%这是必然结果而不是模型好。max_depth、min_samples_split、min_samples_leaf这三个参数至少设一个否则它就是一个完美记住训练集的黑匣子。3.4 评估指标MAE、MSE、RMSE、R² 的量纲与敏感度两份卷子在同一类题上给了不同答案一份问平均绝对误差选 MAE另一份问均方差选 RMSE看着矛盾其实是措辞差异。把四个指标摆在一起看就清楚了指标计算方式量纲对大误差的敏感度典型用途MAEmean(y - ŷ)与 y 相同MSEmean((y - ŷ)²)y 的平方高平方放大作为损失函数优化时RMSEsqrt(MSE)与 y 相同高需要和 y 同量纲对比时R²1 - SSE/SST无量纲中衡量相对基线的解释比例判据是两条要量纲一致、能直接说平均差了几块钱就用 MAE 或 RMSE要拿来当损失函数求导就用 MSE因为平方项求导干净绝对值在零点不可导。RMSE 和 MAE 的差距本身就是一个诊断信号——RMSE 明显大于 MAE说明误差分布里有少数几个偏差极大的样本值得单独拎出来看看是不是异常值或者标注错误。R² 有个容易被忽略的性质它可以为负。当模型的预测比直接猜训练集均值还差时SSE SSTR² 就掉到零以下。看到负数不要以为是计算错了那说明模型彻底没学到东西方向错了。4. 编程题落地线性回归与 KNN 两段能跑的代码编程题部分原卷只要求写步骤不要求写代码。但导入库函数 → 导入数据集 → 建模 → 拟合 → 预测 → 评价这六步如果不落到键盘上考完就忘。这两道题的数据都很小手算都能验正好拿来当核对基准。4.1 简单线性回归六步流程与那行必须加的 reshape数据集是特征X[1,2,3,4,5]、标签Y[3,4,6,8,9]。照着六步写出来的第一版代码八成会在fit那一行崩掉报Expected 2D array, got 1D array instead。原因是 sklearn 的所有估计器都要求输入是二维的(n_samples, n_features)一维列表会被理解成一个样本的多个特征而不是多个样本的一个特征。修法就是reshape(-1, 1)。import numpy as np from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score # 第一步导入数据集。reshape(-1, 1) 把 (5,) 变成 (5, 1)-1 表示行数自动推断 X np.array([1, 2, 3, 4, 5]).reshape(-1, 1) y np.array([3, 4, 6, 8, 9]) # 第二步模型实例化。fit_interceptTrue 表示要估计截距 w0 model LinearRegression(fit_interceptTrue) # 第三步拟合也就是把 w0、w1 解出来 model.fit(X, y) # 第四步预测 y_pred model.predict(X) # 第五步评价 print(w1 , model.coef_[0]) # 斜率 print(w0 , model.intercept_) # 截距 print(MAE , mean_absolute_error(y, y_pred)) print(RMSE , np.sqrt(mean_squared_error(y, y_pred))) print(R2 , r2_score(y, y_pred))这段代码跑出来的结果是可以手算核对的。x 的均值是 3y 的均值是 6协方差项Σ(x-x̄)(y-ȳ) 16x 的方差项Σ(x-x̄)² 10所以斜率w1 16/10 1.6截距w0 6 - 1.6×3 1.2模型就是y 1.2 1.6x。预测值依次是 2.8、4.4、6.0、7.6、9.2残差 0.2、-0.4、0、0.4、-0.2SSE 0.4SST 26R² 1 - 0.4/26 ≈ 0.9846。跑完代码拿这几个数字对一遍比背十遍公式管用。参数上有两点值得说明。fit_intercept默认是 True如果你的数据已经中心化过或者业务上明确要求过原点可以设成 False此时intercept_恒为 0但这个开关不要随手关关掉之后 R² 的计算口径在 sklearn 里也会跟着变容易对不上账。另外coef_是数组单特征时取[0]多特征时它是一个和特征顺序对应的向量特征顺序搞混了会把系数解释反。4.2 KNN 分类从十行数据到一次预测KNN 那题的数据是十个二维点前五个标签 0后五个标签 1。这组数据是人工构造的两类大致按横坐标分开[5,3]属于 1 类而[2,2]属于 0 类中间没有严格的分界线正好用来观察 k 值怎么影响判断。from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score # 导入数据集注意这里是二维特征天然满足 (n_samples, n_features) X [[3, 2], [3, 1], [1, 3], [3, 4], [2, 2], [7, 4], [5, 3], [9, 2], [7, 3], [7, 0]] y [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] # 实例化n_neighbors 就是 kweightsuniform 表示等权投票 knn KNeighborsClassifier(n_neighbors3, weightsuniform, metricminkowski, p2) # 拟合KNN 是惰性学习fit 阶段只是把训练样本存下来不做任何参数计算 knn.fit(X, y) # 预测一个没见过的新点 print(knn.predict([[4, 3]])) # 输出 [0] print(knn.predict_proba([[4, 3]])) # 输出各类别得票比例 # 评价在训练集上算准确率 print(train acc , accuracy_score(y, knn.predict(X)))手算一遍能加深对投票这件事的理解。新点[4,3]到十个样本的欧氏距离分别是[5,3]距离 1.0类 1、[3,2]和[3,4]都是 1.414类 0、[3,1]和[2,2]都是 2.236类 0、[1,3]和[7,3]都是 3.0类 0 和类 1 各一、[7,4]是 3.162类 1、[7,0]是 4.243类 1、[9,2]是 5.099类 1。取最近的三个1.0 一个类 11.414 两个类 0票数 2:1判为 0 类。几个参数直接决定结果。n_neighbors必须是奇数二元分类下偶数会产生平票sklearn 的处理方式是取标签较小的那个类这个规则不写出来基本没人知道结果看起来就像随机。weights取distance时按距离倒数加权近的邻居话语权更大适合类别边界不均匀的数据。p2是欧氏距离p1是曼哈顿距离在高维稀疏数据上后者有时更稳。metric换成cosine可以做文本向量的相似度检索那是 KNN 在推荐场景里的常见用法。注意KNN 完全依赖距离特征量纲不一致时结果会被大数值特征主导。比如一个特征取值在 0 到 1 之间、另一个在 0 到 10000 之间后者会吞掉整个距离计算。上 KNN 之前必须做标准化这是它和树模型最大的流程差别。5. 避坑清单五条翻车记录与修正方式5.1 代码层面的三个坑坑一一维数组直接喂模型报 Expected 2D array。现象是ValueError: Expected 2D array, got 1D array instead栈信息停在各估计器的fit方法里。原因只有一个sklearn 的输入约定是(n_samples, n_features)一维数组只有样本维度没有特征维度。解决方式二选一np.array(X).reshape(-1, 1)适合单特征pd.DataFrame适合多特征且能带列名。判断该用哪个很简单有几个特征就几列。坑二预测结果是嵌套数组或者形状对不上。现象是predict返回的东西打印出来带着双层括号或者拿它和y_test算指标时报形状不匹配。原因是预测输入也需要是二维的传[[4, 3]]得到的是(1,)形状的类别数组传[4, 3]则会报错。解决方式是在预测前统一转成np.array([[4, 3]])批量预测时直接传整个二维测试集不要一个点一个点循环那样慢十倍不止。坑三训练集准确率漂亮得不像话换数据立刻崩。现象是accuracy_score(y, knn.predict(X))返回 1.0一上测试集掉到 0.6。原因通常是 k 取 1——单邻居模型在训练集上对每个样本的最近邻就是它自己准确率必然是 100%这个数字毫无意义。解决办法是把 k 调到 3 以上并且评价一律在留出集或者交叉验证上做。反过来也有一种情况k 取得接近样本总数模型退化成永远预测多数类此时训练集准确率等于多数类占比看起来还行实际上什么都没学到。5.2 概念题层面的两个坑坑四题目问决策树算法有几种把划分标准当成了算法。现象是数出四种甚至五种把信息增益、增益率、基尼指数也算成算法名。原因是没分清算法和划分标准这两个层级。修正是记住一对三的映射ID3 对应信息增益C4.5 对应信息增益率CART 对应基尼指数三个算法三套标准一一对应。另外 ID4 这个选项是编出来的看到它直接排除。坑五聚类算法数量的答案在不同卷子里对不上。现象是同一道聚类分析有几种算法在两份卷子里给了不同选项且答案不同。原因是这类题本身口径不清出题人心里想的可能是教材上列的 K-Means、层次聚类、DBSCAN 三种经典方法也可能是 sklearn.cluster 模块里的类数量。修正是考试时按教材工程上按需求需要指定簇数用 K-Means需要层次结构用层次聚类簇形状不规则且带噪声用 DBSCAN样本量上百万用 MiniBatchKMeans。把这道题当成一个索引记住去哪查比死记数字有用。提示这套卷子里还有若干属于教材口径的题比如神经元模型的标准表述、机器学习的定义表述答案措辞和你在别处看到的可能略有出入。这类题按给定答案为准备考即可不必过度纠结术语的严谨性但要知道工程文档里的说法可能不一样。6. 用交叉验证验一遍背下来的结论KNN 的 k 值该怎么扫第 2 章那张表里写着欠拟合是容量不够过拟合是容量过大这话背下来只要十秒真正理解要动手看一次边界怎么随着容量变化。手边最方便的实验就是 KNN 的 k 值——它就是这台模型唯一的容量旋钮而且方向和其他模型相反k 越小容量越大。拿第 4 章那十个点把 k 从 1 扫到 9同时观察三个量训练集准确率、留出集准确率、对新点[4,3]的预测类别。import numpy as np from sklearn.neighbors import KNeighborsClassifier from sklearn.model_selection import cross_val_score, LeaveOneOut X np.array([[3,2],[3,1],[1,3],[3,4],[2,2],[7,4],[5,3],[9,2],[7,3],[7,0]]) y np.array([0,0,0,0,0,1,1,1,1,1]) probe np.array([[4, 3]]) # 样本只有十个用留一法做交叉验证比切分更能榨出信息 loo LeaveOneOut() for k in range(1, 10, 2): clf KNeighborsClassifier(n_neighborsk) train_acc clf.fit(X, y).score(X, y) cv_acc cross_val_score(clf, X, y, cvloo).mean() pred clf.predict(probe)[0] print(fk{k} train{train_acc:.2f} loo{cv_acc:.2f} probe{pred})跑完会看到一条很典型的曲线。k1 时训练准确率是 1.0这是数学上必然的——每个训练点到自己的距离是 0最近邻永远是自己。但留一法准确率不会这么高因为留出的那个点要靠别人投票边界附近的点经常被投反这就是过拟合的直观形态记住了每一个点却没抓住整体趋势。随着 k 增大到 3、5训练准确率下降留一法准确率反而上来说明模型从记点切换到看区域。k 继续加大到 9训练准确率会掉得很难看因为此时几乎是在用全局多数类做判断退化成欠拟合。新点[4,3]的预测变化更值得盯k1 时它只看最近的那个[5,3]类 1判成 1k3 和 k5 时类 0 的邻居被纳入投票判成 0。同一个点、同一份数据只因为容量旋钮转了一格结论就翻了。这件事说明模型预测从来不是数据的客观属性而是数据加上容量选择之后的联合产物脱离超参数谈预测对错没有意义。这个实验我在本地跑过很多遍每次带人过一遍效果都比讲半小时偏差方差分解好。血泪经验是扫参的时候一定要把训练集指标和验证集指标一起打印出来只看其中一个必然被误导。只看训练集你会一路选到 k1只看验证集你会因为样本量太小而被偶然波动带偏得出一条并不稳的 k 值。从那以后我每次拿到一套题库或者一份结论清单都会挑其中一个能被小数据验证的点写二十行代码跑一遍再往下背——因为背下来的结论会和你的直觉打架跑过一遍的结论才会在真正出问题的时候自己跳出来。希望帮到你。本文还有配套的精品资源点击获取