又到刷题时间。这个系列我自己都想吐槽,说好每周更新一次,结果硬是拖成了"不定期更新"。好在前三次的内容都是实打实的:第一次把Python工具链和NumPy广播机制过了一遍,第二次集中刷了逻辑回归、决策树这些经典算法的推导,第三次用PyTorch完整搭了一个手写数字分类网络。如果你是从第一篇跟到现在的,基础部分基本稳了;如果是中途进来的,直接做第四次这套综合练习,遇到不懂的再回头翻前面的文章补课,效率反而更高。
"AI入试"这个词,我按最宽泛的理解来用:人工智能方向的入学复试、企业算法岗笔试、以及各类AI能力认证考试都算。这类考核有个共同点,就是特别爱考"看起来基础、实际上容易翻车"的问题,而且经常把数学推导、代码实现和概念判断揉在同一道题里。第四次练习我就故意把这种综合题挑出来,做成了带详细解析的练习合集,适合正在备考、或者想检验自己是否真正掌握AI基础的人。整套题不追求偏难怪,而是追求"问到你平时忽略的为什么"。
1. 第四次练习的整体定位与选题思路
1.1 前三次练习留下的"地基"
先简单交代一下系列进度。第一次我带着大家把Python的列表推导式、NumPy的广播机制、矩阵乘法练到闭着眼能写;第二次重点讲逻辑回归和决策树的公式推导,以及它们在真实数据上的表现;第三次主要是用PyTorch搭建全连接网络,把数据加载、模型定义、训练循环完整走了一遍。三轮下来你会发现一个尴尬的事实:单个知识点做题都会,一旦混在一起,脑子就容易卡壳。于是第四次我换了一种玩法,不再按"知识点"为单位出题,而是按"题型"为单位出题——同一个考点,用选择题、推导题、编程题轮番轰炸,看看自己是不是真懂了。
这也是我备考AI入试最真实的需求:考试不会只问"什么是过拟合",它一定会让你判断"这个现象是不是过拟合",或者让你想办法减轻过拟合。所以这次练习,每一类题我都尽量做成"概念+推导+场景"的复合结构,而不是孤立的记忆题。做题时你可以先独立完成一遍,再看后面的解析,这样效果比直接阅读答案好得多。
1.2 为什么这轮选题主打"综合+手推"
认真研究过几套相关笔试和复试题目之后,我发现出题人有一个共同偏好:希望筛选出"真懂原理"而不是"背住结论"的人。所以概念题后面往往跟着一个"为什么",计算题又要求你写出中间步骤。给我的启发是:平时刷题不能只盯着答案对错,而是要练推导速度和表达的严谨性。
第四次的四类题型,我是这样规划的:
| 题型 | 考察能力 | 本次题量 | 建议用时 |
|---|---|---|---|
| 概念辨析题 | 是否真正理解核心术语 | 3道 | 每题3分钟 |
| 手推计算题 | 能否独立完成公式推导 | 2道 | 每题8分钟 |
| 公式应用题 | 能否把数学知识落到场景 | 3道 | 每题5分钟 |
| 编程实现题 | 能否把算法转成代码 | 1道 | 12分钟 |
选择题检验"你知不知道",简答题检验"你能不能讲清楚",计算题检验"你敢不敢动手算",编程题检验"能不能把公式变成可执行代码"。四类都过一遍,才算真正掌握一个考点。这套思路你在准备任何技术考核时都可以复用。
2. 机器学习基础:概念题与易错点复盘
2.1 类别不平衡下,指标选择是个送命题
先上这道选择题,它在我刷过的题目里出现频率极高。
题目:在一个二分类任务中,正样本仅占1%,你觉得哪个评估指标最不靠谱?
A. Accuracy
B. F1-Score
C. AUC
D. Recall
正确答案是A。原因很简单:如果模型把所有样本都预测为负类,Accuracy能到99%,可这个模型一点用都没有。用生活化的话说,就像天气预报天天报"不下雨",一个月里虽然只错了两三回,但我们想要的从来不是这种"平均正确率"。在正负样本严重失衡的业务场景里,准确率会被多数类主导,完全掩盖少数类上的表现。
F1-Score是精确率和召回率的调和平均,在这种场景下能反映模型对少数类是否真的捕捉到了;AUC看的是正负样本排序能力,受不平衡影响相对较小;Recall则直接关心"真正的正样本我捞回来多少"。所以看到"类别不平衡"五个字,第一反应应该是排除Accuracy,优先考虑F1、AUC、PRC。这里特别提醒:PRC在正样本极少时比ROC更敏感,很多参考答案不提这一点,但你答出来会是加分项。
再补一道延伸思考:如果业务场景是"识别交易欺诈",正样本就是欺诈交易,非常稀少,你会更看重Precision还是Recall?答案取决于代价。如果一次误判会把正常用户冻住,损失很大,那就往Precision偏;如果要尽可能抓住所有欺诈,不管误伤,就主攻Recall。所以考试里不要只背指标公式,还要学会结合场景说取舍。
2.2 偏差-方差分解,简答题这样答才能拿全分
第二道高频简答题:请用偏差-方差分解解释过拟合与欠拟合。
我的建议答题框架分四步。第一步直接写公式:泛化误差 = 偏差^2 + 方差 + 噪声。第二步说清偏差和高低:欠拟合模型太简单,对训练数据都拟合不好,属于高偏差、低方差;过拟合模型把训练数据里的噪声都学进去了,属于低偏差、高方差。第三步举一个具体例子佐证,比如用线性模型去拟合二次函数,偏差很大、方差不大;用九次多项式拟合少量点,偏差小、方差极大。第四步给对策:高偏差时增加模型复杂度、加特征、减少正则化;高方差时增加训练数据、降低模型复杂度、加正则化、做交叉验证。四步说完,基本就是个小满分答案。
我在批改自己练习时发现一个常见错误:很多同学把"偏差"理解成"预测值和真实值的差",这没错,但在机器学习语境下,偏差描述的是模型在大量不同训练集上的平均预测与真实值的差异;方差描述的是模型在不同训练集上的变动程度。这两个概念一定要从"多次采样训练集"的角度理解,否则一旦被追问"为什么增加数据能减小方差"就会卡住。事实上,数据增多时,模型见过的样本分布更接近真实分布,它对训练集具体样本的依赖变小,方差自然下降。
2.3 梯度下降的"变体们"别只记名字
这种题通常以填空或选择形式出现。比如:比较批量梯度下降(BGD)、随机梯度下降(SGD)和小批量梯度下降(Mini-batch GD)的优缺点。
批量梯度下降每次用全量数据计算梯度,方向最准,但数据量大时速度慢,而且容易停在局部极小;随机梯度下降每次用一个样本更新,快但震荡剧烈,带噪声反而有机会跳出局部极小;小批量梯度下降折中,每次用一小批,兼顾稳定与速度,是实际深度学习训练最常用的方式。答题时最好补一句:现代框架里的SGD默认指小批量随机梯度下降,而不是严格意义上的单样本更新。这种"抠定义"的小细节,很容易让阅卷人觉得你确实动过手。
这里再补充一个常被追问的点:学习率怎么设。学习率太大,损失曲线会震荡甚至发散;太小又收敛慢。常见的做法是用余弦退火或warmup加动态衰减,初始学习率一般取0.01到0.001之间,再根据验证集损失做调整。回答这类问题时,只要把"控制更新步长"这个本质讲明白,再给出一个具体量级和调整方向,会显得很有实操感。
3. 深度学习与神经网络:手推反向传播
3.1 从零手推一个神经元,不再怕链式法则
AI入试里,手推反向传播基本是必考项。很多同学一看公式就头大,其实只用记住一句话:梯度是损失对参数的偏导,链式法则就是"逐层求导再相乘"。我们来看经典的最小例子。设输入 x=2,目标 y=0.5,初始参数 w=1.0,b=0.5,激活函数是Sigmoid,损失函数是均方误差MSE。前向传播的计算为:
z = wx + b = 1.0 × 2 + 0.5 = 2.5 a = sigmoid(z) = 1 / (1 + e^(-2.5)) ≈ 0.924 L = 0.5 × (a - y)^2 = 0.5 × (0.924 - 0.5)^2 ≈ 0.0899
反向计算 dL/dw 时,按照链式法则拆成三段:
dL/da = a - y ≈ 0.424 da/dz = a(1 - a) ≈ 0.924 × 0.076 ≈ 0.0702 dz/dw = x = 2
相乘得到 dL/dw ≈ 0.424 × 0.0702 × 2 ≈ 0.0595。偏置的梯度是 dL/db ≈ 0.424 × 0.0702 ≈ 0.0298。如果学习率设为0.1,参数更新为 w_new ≈ 1.0 - 0.1 × 0.0595 = 0.99405,b_new ≈ 0.5 - 0.1 × 0.0298 = 0.49702。
手推这种题有几个坑。第一个是搞混损失对输出的导数:交叉熵配合Sigmoid时推导会简洁很多,但这里用的是MSE,必须老老实实算。第二个是参数更新的方向:梯度下降是减去梯度,方向别反。第三个是Sigmoid在z较大时梯度接近0,这就是梯度消失的雏形,手推题里如果激活输出接近1或0,梯度值会非常小,要能意识到并说明。我的习惯是前向把 z 和 a 分别存成中间变量,反向直接取用,这样既快又少出错。备考时建议把"用乘法链写清每一步偏导"当成固定书写格式,阅卷人看着清晰,自己也容易发现错误。
3.2 特征图尺寸与感受野:别再被padding和stride绕晕
这个知识点几乎每次笔试都会出现,考法通常是给输入尺寸、卷积核大小、padding和stride,让你算输出特征图尺寸,或者反过来求某个参数。
通用公式是:
输出尺寸 = floor((W + 2P - F) / S) + 1
其中W是输入边长,P是padding单边填充数,F是卷积核大小,S是stride步长。为什么加2P?因为padding是在左右两侧都填充。为什么最后加1?因为卷积核要覆盖到最后一个可行位置,类似"从0开始的计数"。用floor还是ceil取决于框架,PyTorch默认是floor,手算结果和机器结果一致。
比如输入32x32,3x3卷积,padding=1,stride=2:先算32 + 2×1 - 3 = 31,除以2得到15.5,向下取整15,再加1得到16,输出是16x16。再堆一层同样参数:16 + 2 - 3 = 15,除以2得7.5,取整7,加1得8,输出8x8。很多同学在15.5这里直接四舍五入成16,然后忘记再加1,得出17,这是最常见的错误。我建议用直觉检查:stride为2时,输出尺寸大致接近输入的一半。32变成16,符合直觉;如果算出来是17或33,大概率是取整和加1的顺序错了。
池化层公式通常是 floor((W - F) / S) + 1,没有padding。比如14x14输入,2x2池化,stride=2,输出是7x7,因为(14-2)/2+1=7。真题里还喜欢让你比较"最大池化"和"平均池化"在梯度传播上的差异:最大池化梯度只会回传到前向中选中的那个位置,平均池化则把梯度均匀分配到各位置。这些小点都答上,说明你不是只会算尺寸。
3.3 用感受野解释为什么CNN喜欢堆小卷积核
再补充一道简答题:感受野是什么意思?怎么理解"为什么很多网络堆好几层3x3卷积,而不是直接用一个大卷积核"?
感受野指某层特征图上每个像素对应原图区域的尺寸。两个3x3卷积堆叠,特征图上的一个点其实可以看到原图5x5的区域,参数量却是2×(3×3×C×C)=18C^2,而一个5x5卷积的参数是25C^2。所以小卷积核堆叠在相同感受野下更省参数,还引入了更多非线性层,表达能力更强。计算参数量时注意不要漏掉输入输出通道:单个卷积核的参数量是F×F×C_in×C_out,有偏置还要加C_out。以上就是这类简答题的完整答法,先给定义,再给参数对比,最后说非线性带来的收益。
4. 数学基础与编程实战:从推导到实现
4.1 特征值特征向量,PCA的前置考点
矩阵特征值是线性代数部分的重头戏,考法很直白:给你一个2x2矩阵,求特征值和对应的特征向量。例题:A = [[2,1],[1,2]]。
先写特征方程 det(A - λI) = 0:
(2-λ)(2-λ) - 1×1 = 0 化简得 (2-λ)^2 - 1 = 0 解得 λ = 1 或 λ = 3。
λ=1时,代入 A - I = [[1,1],[1,1]],解方程 (A-I)v=0 得到 x1 + x2 = 0,所以取一个特征向量 v = [1,-1],单位化就是 [1/√2, -1/√2]。λ=3时,代入 A - 3I = [[-1,1],[1,-1]],解方程得到 x1 - x2 = 0,取 v = [1,1],单位化 [1/√2, 1/√2]。
这一题在考试里往往只是起点,后续会问:用特征值大小说明矩阵性质。最大特征值3对应方差最大的方向,如果把这个矩阵当成某个数据的协方差矩阵,主成分方向就是 [1,1] 方向,它解释了总方差的 3/(3+1)=75%。这就是PCA的底层逻辑:找到协方差矩阵特征值最大的几个方向,实现降维。
我备考时总结的答题模板是:写特征方程、求λ、逐个代回求特征向量、说明几何含义。千万不要只求λ就完事,特征向量才是后续问题的关键。另外注意,特征向量可以乘任意非零常数,写"一个特征向量为...,也可乘以任意非零常数",既严谨又能避免和标准答案有出入。
4.2 信息熵与基尼指数:决策树计算题怎么拿分
决策树计算题在AI入试中属于送分题,前提是公式记熟并能手算。给一个常见考法:给定划分前后的纯度指标,判断哪个特征更好。假设一个二分类数据集有15个样本,其中正例9个、负例6个。某个特征把数据划分为两个子节点:第一个子节点包含6个样本,正负各3个;第二个子节点包含9个样本,正例6个、负例3个。请计算基尼指数。
先算划分前的基尼指数:
Gini(D) = 1 - (9/15)^2 - (6/15)^2 = 1 - 0.36 - 0.16 = 0.48
划分后按样本量加权:
Gini(D1) = 1 - (3/6)^2 - (3/6)^2 = 0.5 Gini(D2) = 1 - (6/9)^2 - (3/9)^2 = 1 - 4/9 - 1/9 ≈ 0.444 Gini_after = (6/15)×0.5 + (9/15)×0.444 ≈ 0.467
基尼系数下降量 = 0.48 - 0.467 = 0.013。做特征选择时,应该选让基尼系数下降量最大的特征。同样,如果题里用信息熵,就是用信息增益表示下降量。这两种指标本质都衡量"划分前后不确定性减少多少"。
这类题最容易出错的地方有两个:一是忘记按样本数量加权,直接把两个子节点的纯度做平均;二是把"基尼指数最小"和"基尼指数下降量最大"搞混。决策树在CART里建树时,是遍历特征和阈值,选择下降量最大的切分点,所以如果问"哪个特征更好",比的是下降量,不是节点纯度的绝对值。我在实际练习中会把这类题写在错题本上,重点标记"加权平均"四个字。
4.3 朴素贝叶斯:一个条件概率的应用题
朴素贝叶斯高频考点集中在贝叶斯公式和"朴素"的由来。例题:已知 P(A|B)=0.8,P(B)=0.3,P(A|非B)=0.2,求 P(B|A)。
根据贝叶斯公式:
P(B|A) = [P(A|B)P(B)] / [P(A|B)P(B) + P(A|非B)P(非B)] = (0.8×0.3) / (0.8×0.3 + 0.2×0.7) = 0.24 / 0.38 ≈ 0.6316
这道题考的不只是公式,还考分母是否写全。分母里必须包含互补条件下的全概率项,也就是 P(A|非B)P(非B),很多同学会漏掉这一项。另一个常考的点是"朴素"二字的含义,即特征之间条件独立的假设:P(x1,x2|y)=P(x1|y)P(x2|y)。回答时说明这个假设简化了联合概率计算,虽然实际很难完全满足,但很多场景下效果依然不错,所以被称为"朴素"。
4.4 编程题:手写K折交叉验证,不调库
最后是一道代码题。我选了K折交叉验证,因为它在笔试里出现频率高,而且手写能检验你对数据切片、随机种子、循环流程是否真正理解。用NumPy手写一个简单的切分函数:
import numpy as np def kfold_split(n_samples, k, shuffle=True, seed=42): index = np.arange(n_samples) if shuffle: rng = np.random.default_rng(seed) rng.shuffle(index) # 每折至少 n_samples // k 个样本,余数从第一折开始分配 fold_sizes = np.full(k, n_samples // k, dtype=int) fold_sizes[: n_samples % k] += 1 folds = [] current = 0 for fold_size in fold_sizes: folds.append(index[current:current + fold_size]) current += fold_size return folds使用逻辑很简单。n_samples=20、k=5时,每折大小是4;n_samples=23时,前3折大小为5,后2折大小为4。训练循环里,把验证折以外的所有索引拼成训练集:
folds = kfold_split(23, 5) for i in range(5): val_idx = folds[i] train_idx = np.concatenate([folds[j] for j in range(5) if j != i]) # 在这里训练模型,用 train_idx 训练,用 val_idx 验证注意 np.concatenate 传入的是一个列表,列表里每个元素是一个数组,不要用列表相加,否则会变成嵌套而不是扁平化。实践里最常见的错误是忘记shuffle,导致有序数据直接按类别块切分,验证集就会全是同一类,评估结果虚高或虚低。另一个细节是使用 np.random.default_rng(seed) 而不是 np.random.seed + np.random.shuffle,这样可以避免污染全局随机状态,在多次重复实验时尤其重要。如果笔试要求的是严格的Stratified KFold,还需要按类别比例逐层分配样本,手写复杂度会高很多,先写出基础版并说明"若要保证类别比例还需分层抽样",已经能拿到大部分分数。
5. 易错点与应试策略踩坑记录
5.1 AI入试笔试最常见的三个丢分位置
根据我自己的练习记录和身边备考者的反馈,丢分往往不在难题,而在基础题三个位置。第一个是概念混淆:Bagging和Boosting搞反、L1和L2正则化说不清、归一化和标准化用错场景。解决方法是画一张对比表,把相似概念的区别写清楚,考前只看这张表。第二个是计算粗心:特征图尺寸公式里漏了padding的2倍、特征值方程展开错符号、基尼指数忘记加权。解决方法是每道计算题都先写公式再带入,不要跳步。第三个是代码细节:手写交叉验证时忘记shuffle,梯度下降里把梯度和参数更新方向搞反。这些坑在平时刷题时发现,比考试时丢分更值钱。我建议把错题按"概念型/计算型/代码型"分类记录,考前只看错误原因栏,效率极高。
5.2 我的答题顺序与时间分配建议
AI入试这类综合性笔试,时间通常比较紧。我的习惯是先用2分钟扫一遍所有题目,标记哪些是需要长时间计算的、哪些是概念题。做题顺序上先做计算题和编程题,因为它们需要大脑在高唤醒状态下手算,越做到后面越容易疲劳和粗心;概念简答题放中间,用来调剂节奏;最后留10分钟检查计算题的符号和公式。
单题时间控制上,选择题不超过3分钟,简答题5分钟左右,计算题可以放到8分钟,编程题最多15分钟。如果一道题卡了超过预计时间的一半,果断先跳过,后面有时间再补。这套节奏在多次模拟练习里让我保持了较高完成度,推荐你也试试。你可以根据自己强弱项微调顺序,但核心原则是:不要在低分值的难题上耗尽脑力,影响后面更多基础题的得分。
5.3 如何把错题变成真正属于自己的东西
我不太建议反复刷同一套题,那会产生"假性记忆"。更有效的做法是把做错的题改造成变体:比如把K折交叉验证里n_samples从23改成100,要求写出每折样本数;把特征图尺寸里的padding改成0,重新算一遍。这样比再做一次原题更能检验你是否真的掌握。
我也习惯在题干旁边写一句话,标记这道题在真实项目中对应什么场景。比如特征图尺寸计算,对应"改网络结构时确认维度是否匹配";反向传播手推,对应"调参时理解梯度异常来自哪里"。备考不是任务,而是把知识内化成工具。带着这个心态,AI入试的题目会越做越顺,而且工作里遇到类似问题,你也更容易想到用哪个知识点去解。
第四次练习到这里就结束了。说实话,每次整理这些题,我自己也能复习一遍,尤其是手推反向传播和交叉验证,哪怕写过很多遍,偶尔还是会踩到符号和维度的坑。所以我的个人建议是:题不在多,把每一道综合题真正手推、手写一遍,比泛泛刷十道题更有用。下次第五次,我打算加入Transformer的Self-Attention手推和常见激活函数对比,如果你已经把这次的内容消化完,我们下一步见。