ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

计算机视觉算法实习生笔试题全解析:核心考点与备赛攻略

计算机视觉算法实习生笔试题全解析:核心考点与备赛攻略 又到了一年一度的实习生招聘季不少学弟学妹跑来问我“计算机视觉算法实习生”的笔试到底考什么。我翻出当年整理过的网易2018实习生招聘笔试题计算机视觉算法实习生方向结合后来几年在校招里反复出现的高频考点重新梳理了一份完整回顾。这篇文章不打算只贴题和答案而是把每一类题目背后的知识点、答题思路、编程题的可复现代码以及我自己的踩坑经历都一并写出来。无论你现在是大三准备投暑期实习还是研一想提前摸清企业笔试的路数这趟梳理应该都能给你省下不少瞎琢磨的时间。1. 整体题目框架分析与出题风格先说结论这类笔试题的核心目的不是筛选谁“背的书多”而是筛掉那批连基础都没打牢、拿到问题完全没有思路的人。网易2018年那套计算机视觉算法实习生的笔试试卷整体结构可以分成三块计算机基础与数据结构、机器学习与深度学习基础、经典算法编程题。前两块是客观题选择、判断、填空的形式最后一块是OJ在线编程通常有两道题一道偏思维一道偏实现。1.1 为什么计算机视觉岗要考通用算法很多同学不理解我投的是CV方向为什么要做KMP、快速排序、动态规划这些“后端岗才考的题”我当年也这么吐槽过后来自己参与过两次组内的实习生面试才明白企业的真实逻辑。算法题的本质不是考“你会不会背这道题”而是看你的“问题拆解能力”。视觉算法里处处要跟时间复杂度、空间复杂度打交道训练样本多了DataLoader的读取效率就是全局瓶颈图像预处理时一个Resize是跑在CPU还是GPU上直接决定你一个epoch要等多久做特征匹配时如果暴力求解几千个特征点之间两两比较的复杂度根本扛不住。这些场景下如果你对数据结构没有直觉连优化方向都找不到。所以笔试里塞数据结构、排序、字符串匹配不是在为难你而是在模拟你以后工作里“需要把某个模块跑快一点”的真实需求。1.2 笔试题型分布与时间分配那套卷子整体时间大概是90到120分钟具体分配我印象里是这样题型题量建议用时重点内容单选题15-20题25-30分钟数据结构、机器学习基础、深度学习基础多选题5-8题10-15分钟图像处理、CNN细节、损失函数简答/填空3-5题10-15分钟算法原理简述、公式推导编程题2题40-60分钟经典算法、搜索、DPL这里有一个特别关键的策略前面客观题千万不要恋战。我见过太多同学在一道“SVM核函数选择”的多选题上抠了10分钟结果编程题没时间写最后编译都没通过直接白给。客观题分值小且容易靠第一直觉拿分编程题一题就是二三十分这个性价比差距你自己掂量。2. 核心知识点拆解数据结构与基础算法这一part虽然看起来像是“通用题”但其实就是考察你代码功底的地基。我挑几个在那年试卷里出现频次极高、后来校招也反复出现的点来讲清楚。每个知识点我都尽量把“原理-为什么考-怎么应用”串起来说这样你复习的时候就不需要死记硬背了。2.1 KMP算法与next数组的计算这个考点当年精准命中了一个填空“在KMP算法中对于模式串 pabacaba其next数组next[i]定义为…”。这种题属于典型“背过就会、没背就懵”的类型但它考查的本质是你对字符串匹配“回退逻辑”的理解。KMP的核心思想是当主串的某个字符和模式串失配时不需要把模式串整体右移一位重新匹配而是根据已经匹配的部分把模式串跳到下一个可能匹配的位置。这个“跳”的依据就是next数组。next[i]的定义是模式串的前i个字符组成的子串中最长相同前后缀的长度注意这里不同的教材定义略有出入有的next数组从-1开始有的从0开始你考试时一定要看清楚题目给的起始下标定义不然一错全错。以pabacaba为例手动推一遍next数组按next[i]表示前i个字符的最长公共前后缀长度、且next[0]-1、next[1]0的常见定义i0规定为-1。i1子串a没有真前后缀next0。i2子串ab前缀a后缀b不相等next0。i3子串aba前缀a等于后缀a长度为1再看ab和ba不相等所以next1。i4子串abac最长相同前后缀a和c不等ab和ac不等aba和bac不等next0。i5子串abacaa和a相等长度1ab和ca不等aba和aca不等abac和baca不等next1。i6子串abacaba和b不等ab和ab相等长度2aba和cab不等再长也不匹配next2。i7子串abacabaa和a相等长度1ab和ba不等aba和aba相等长度3abac和caba不等所以next3。最后得到next数组[-1, 0, 0, 1, 0, 1, 2, 3]。考试时这类题只要你把“最长相同前后缀”的定义理解透即使现场忘了怎么编代码也能手算出来。平时复习的时候建议自己再推一遍pababaca、paaaaab推完你就有感觉了。2.2 排序算法复杂度横向对比那年试卷里出现了一道选择题列出冒泡、快排、堆排、归并的时间复杂度让选最优的排序算法。这道题没坑但暴露了一个问题很多同学只记得“快排平均O(nlogn)”却不知道快排最坏是O(n^2)也不清楚什么时候会触发最坏情况每次选的pivot恰好是最大或最小值。这种半吊子记忆在校招笔试中是非常危险的。我把常用排序的关键点整理成一个表格你按这个表复习基本不会漏算法平均时间最坏时间空间稳定性适用场景冒泡O(n^2)O(n^2)O(1)稳定基本有序的小数组快速排序O(nlogn)O(n^2)O(logn)不稳定普遍场景数据随机性好归并排序O(nlogn)O(nlogn)O(n)稳定需要稳定性的外部排序堆排序O(nlogn)O(nlogn)O(1)不稳定内存严格受限的场合计数排序O(nk)O(nk)O(k)稳定小范围整数我实习时真遇到过一个场景要对几万张图的预测分数排序后做NMS非极大值抑制因为分数是浮点数且范围固定我一开始用了快排发现速度还行后来换成计数排序处理量化到整数的score确实快了一些但前提是量化误差不能影响NMS阈值判断。这个例子想说的是排序算法不只是笔试里的题目它在你日后的CV工程里真的有出场机会。2.3 贪心与动态规划的分寸拿捏编程题里有一道我印象很深大概是给一组区间要求选尽量多的互不重叠区间。一眼看过去像个贪心题实际上也确实是用贪心解的按区间右端点排序然后依次取。贪心算法的核心是“每一步做当前最优选择且局部最优能推出全局最优”。能证明贪心正确性的题目往往有很强的结构比如活动安排问题、哈夫曼编码、最小生成树。而动态规划则不同它面向的是“当前选择会影响未来选择”的场景你没法只盯着眼前最优。判断一道题到底该用贪心还是DP诀窍就一条当前选择是否受之前选择留下的状态约束。如果不受约束大胆贪心如果受约束想想能不能定义出状态和转移方程。笔试时贪心题往往就是快刀斩乱麻的题而DP题是用来拉区分度的。那年还有一道关于最长递增子序列的变种单独靠O(n^2)的DP能过60%的case但数据量一大就超时需要上贪心二分的优化到O(nlogn)。这种“基础DP保底 进阶优化吃满”的编程题设计几乎是大厂笔试的标配套路后面编程题部分我再展开代码。3. 机器学习与深度学习基础考点聚焦既然岗位名字是“计算机视觉算法”机器学习和深度学习的基础概念当然躲不掉。这一部分的难度大多停留在“知道概念、理解为什么”的层面不会要求你从零手推一篇论文但如果连基础概念都模棱两可基本就告别Offer了。3.1 传统机器学习的高频概念那套卷子里关于传统机器学习的考点主要集中在几个地方SVM的核函数、随机森林的构建流程、过拟合的解决方案、交叉验证、精确率和召回率。其中我觉得最值得展开的是“分类问题的评价指标”这块因为它除了是笔试题还是你入职后每天要面对的东西。精确率Precision和召回率Recall这个经典“此消彼长”的组合考法通常是给你一个分类结果表格让你算准确率、精确率、召回率、F1。每次面试我都会先问自己一个问题对当前这个任务到底是Precision重要还是Recall重要做医疗影像的肿瘤检测漏诊一个患者低召回的代价远远大于误报一个低精确所以要优先保证Recall而做自动驾驶的障碍物识别误检一个不存在的障碍物导致急刹车低精确危害其实比漏检一个远处的目标还大因为急刹车可能引发追尾。所以不同的应用场景下同一套模型调优的方向完全相反。笔试不会考这么深但面试官很可能会顺着你笔试的答案追问这一层。随机森林这块有个必背的点它为什么比单棵决策树稳两个随机性——样本随机抽样有放回和特征随机选择再加上多棵树投票/平均每棵树之间相关性低、偏差略高但方差显著降低从而整体泛化能力更强。很多人背了“bagging降低方差”这句话但不理解“为什么是降低方差”你可以这么理解单棵决策树容易对训练集的噪声过度敏感一点小扰动就会让分裂点大变方差很高而好多棵树各自用不同的样本子集和特征子集去学习大家的“错误”方向不完全一致平均之后噪声被互相抵消了所以整体方差降下来了。3.2 深度学习基础与CNN细节CV岗笔试绕不开CNN相关的基础概念那年出现的题包括卷积层参数量计算、感受野、池化层的作用、梯度消失与梯度爆炸、Batch Normalization的作用。这些都属于“会算不会算”的送分题。卷积层参数量的计算公式特别简单输出通道数 ×输入通道数 × 卷积核高 × 卷积核宽 1个偏置。比如输入是224×224×3第一层卷积核3×3输出通道64那这层参数量就是64×(3×3×31)1792。是不是很简单但很多人算漏了偏置项或者把输入通道数算成224结果差了十万八千里。这种基础计算题看似简单背后其实是希望你具备“评估模型大小”的工程直觉。我在实际部署模型到嵌入式设备时经常要先估算每层参数和FLOPs才决定怎么裁剪模型。笔试里要是连这都算错面试官会怀疑你能不能独立做模型压缩。感受野这个点也值得好好理解。感受野的定义是输出feature map上一个像素对应到输入图像上的区域大小。它有个递推公式RF_{l} RF_{l-1} (k_l - 1) × stride_{l-1累乘}处理空洞卷积时还要考虑dilation。有一种简化记忆方法每经过一个kernel大小为k、stride为s的层感受野变成原来的s倍再加上(k-s)。实际调参的时候比如你用空洞卷积去替代下采样层目的就是在不缩小feature map尺寸的前提下扩大感受野笔试里不会让你手推太深的网络但至少你要能算3到4层的叠加。Batch Normalization是我特别想多说两句的点。它的思想是网络训练过程中每层输入分布一直在变导致上层参数稍微动一下下层就要重新适应收敛自然慢。BN的做法是在每个batch内对特征做归一化让数据均值为0、方差为1然后再通过可学习的缩放和平移系数恢复表达能力。这样做的好处除了加速收敛还能让你用更大的学习率、缓解梯度饱和。但要注意在BN提出之前的年代大家做视觉任务的网络都是靠小学习率、小心初始化去硬扛Internal Covariate Shift的所以理解了BN是在解决什么问题才算真正理解了这个结构。4. 经典编程题完整实战与易错点复盘重头戏来了。那一年的两道编程题整体难度属于LeetCode中等偏下但想在笔试环境下ACAccepted还是需要平时有足够的训练量。我这里还原两道与当年题目思路高度一致的经典题给出完整的思考过程和代码实现。4.1 区间调度问题贪心排序的边界处理题目描述给定n个区间[l_i, r_i]选择尽量多的区间使得它们两两互不重叠区间端点重合也不算重叠即选完[a,b]和[b,c]是允许的。输出最多能选多少个。解题思路这道题的正解是按右端点从小到大排序然后从左往右遍历维护一个变量last_end记录上一个被选区间的右端点如果当前区间的左端点 last_end就选择它并更新last_end 当前区间右端点。很多人会问为什么按右端点排序而不是按左端点我举个反例就能说服你。假设区间是[1, 10]、[2, 3]、[4, 5]。如果按左端点排处理的顺序会是[1, 10]、[2, 3]、[4, 5]第一步选了[1, 10]之后后面两个都没法选了最后答案只有1但按右端点排先选[2, 3]再选[4, 5]答案就是2。原因在于右端点更小的区间留给后面区间的空间越大这是贪心选择性质的关键——按右端点排序能保证每次选择都是“结束最早”的合法区间从而给未来留下最多的余地。代码实现C#include bits/stdc.h using namespace std; int main() { int n; cin n; vectorpairint, int intervals(n); for (int i 0; i n; i) { cin intervals[i].first intervals[i].second; } sort(intervals.begin(), intervals.end(), [](const pairint, int a, const pairint, int b) { return a.second b.second; }); int cnt 0; int last_end INT_MIN; for (auto seg : intervals) { if (seg.first last_end) { cnt; last_end seg.second; } } cout cnt endl; return 0; }易错点复盘排序的lambda表达式里如果两个区间右端点相同最好再按左端点从小到大排避免不稳定排序带来的奇怪结果。判断条件是seg.first last_end意味着边界重合可以选这是题目明确“不重叠”定义的关键点千万别随便把等于号去掉。初始值last_end要设成INT_MIN或者负无穷否则第一个区间左端点正常是正数会被误判为重叠。4.2 最长递增子序列从O(n^2)到O(nlogn)的优化题目描述给定一个长度为n的数组nums求最长严格递增子序列的长度。子序列不要求连续。基础思路定义dp[i]为“以nums[i]结尾的最长递增子序列长度”转移方程是dp[i] max(dp[j] 1)其中j i且nums[j] nums[i]。时间复杂度O(n^2)。这种做法能通过60%-80%的case但数据范围一旦到10^5就超时。进阶思路维护一个数组tails其中tails[i]表示“长度为i1的递增子序列的末尾元素的最小值”。遍历nums的每一个数用二分查找找到tails中第一个大于等于当前数nums[i]的位置把它替换掉如果找不到就追加到末尾。最终tails的长度就是最长递增子序列的长度。为什么这样能做到O(nlogn)核心在于tails是单调递增的所以可以在tails上做二分搜索。这个过程很多人会困惑tails里的元素不是“真正的最长递增子序列”它只是“相同长度下末尾最小”的维护。举个例子nums [10, 9, 2, 5, 3, 7, 101, 18]遍历过程中tails的变化是[10] - [9] - [2] - [2, 5] - [2, 3] - [2, 3, 7] - [2, 3, 7, 101] - [2, 3, 7, 18]长度4正确。虽然tails里存储的序列(2, 3, 7, 18)不一定真的是原始数组中的某个递增子序列但长度是对的因为每次替换都不会改变“长度”的真实性。代码实现C#include bits/stdc.h using namespace std; int main() { int n; cin n; vectorint nums(n); for (int i 0; i n; i) cin nums[i]; vectorint tails; for (int x : nums) { auto it lower_bound(tails.begin(), tails.end(), x); if (it tails.end()) { tails.push_back(x); } else { *it x; } } cout tails.size() endl; return 0; }易错点复盘题目要求是“严格递增”所以要用lower_bound找第一个 x 的位置而不是upper_bound找第一个 x 的位置。如果允许非严格递增那就要换成upper_bound。tails数组的size就是答案但tails内容不是答案序列。如果笔试题让你输出具体子序列这个方法不能直接用需要额外记录回溯信息。笔试环境里如果你对lower_bound的用法记不牢先用O(n^2)的DP保底、再尝试优化这是我最推荐的策略保证有分拿再追求满分。4.3 笔试现场的时间分配与调试技巧关于编程题我还有一个更重要的经验想分享在笔试中先把每道题的暴力解写出来哪怕只过一部分case也比死磕最优解最后交白卷强得多。原因是大部分OJ是“通过多少测试点给多少分”的机制60%的case拿60%的分远比0分要好。而且很多所谓的“暴力解”其实只需要你有最基础的循环和递归能力不需要很高深的思想。具体到时间段分配我个人习惯是前10分钟把两道题都看一遍大致判断哪道简单、哪道难。然后先用15到20分钟把简单题的完整解法写出来并跑通样例接着用30到40分钟死磕难题如果40分钟还没有头绪果断回头检查前面的客观题和已写的代码而不是一昧耗在难题上。笔试考的不仅是会不会还有“有限的90分钟里你能不能最大化得分”这个能力在以后的工作里同样重要。5. CV专项知识点图像处理与特征提取除了通用算法和机器学习这毕竟是“计算机视觉算法”岗图像处理和特征工程一样会占一定分值。我挑几个几乎年年出现的知识点展开——它们不仅是面试题还是你以后做视觉项目天天要打交道的底层概念。5.1 图像滤波与边缘检测图像滤波年年考不外乎均值滤波、高斯滤波、中值滤波、Sobel算子、Laplacian算子。要理清的是均值和高斯是线性滤波常用于去噪但会模糊边缘中值滤波是非线性滤波对椒盐噪声效果极好且能保留边缘所以被大量用于预处理管线里Sobel算子是一阶微分算子用于计算图像梯度是Canny边缘检测的第一步Laplacian是二阶微分算子对噪声敏感所以实际操作中往往先高斯模糊再拉普拉斯也就是LoG的简化思想。笔试里如果给你一个3×3的核让你算某个像素点滤波后的值本质就是“中心像素邻域内做加权求和”注意边界像素通常要补零或使用replicate填充不同填充方式会导致输出值不同。这些细节我特别提醒一句真到做项目的时候边界处理对最后的检测结果有影响尤其是小分辨率图像比如在医学影像上做分割时补零和镜像填充的结果差异肉眼可见。5.2 直方图均衡化与图像增强直方图均衡化是笔试填空和选择题常客核心原理是把灰度分布拉伸到尽可能均匀让图像对比度更高、细节更清楚。它做的操作是把原图像的灰度累积分布函数CDF映射到目标灰度范围new_gray round((CDF(gray) - CDF_min) / (M*N - CDF_min) * (L-1))。是不是只看公式有点抽象我用一个最简单的例子如果一张图整体偏暗大多数像素灰度集中在0到50它的CDF在低灰度区域增长很快、在高灰度区域几乎不变均衡化之后原本密集的暗部灰度被拉伸到整个0到255范围暗部细节自然就出来了。这个变换的缺点是会让图像出现“过增强”噪点也被放大。如果你做的是后续要接深度学习模型的数据预处理均衡化有时反而不如不做因为CNN对光照变化有很强的鲁棒性强行增强反而可能改变语义信息。这一点在真实项目里非常值得注意。5.3 SIFT、HOG与特征匹配基础SIFT尺度不变特征变换和HOG方向梯度直方图在2018年的笔试试卷里出现过选择题主要是考它们各自对什么变换保持鲁棒。SIFT对尺度变化、旋转、光照变化都有较好的不变性因为它在构建尺度空间时用高斯差分金字塔在关键点描述时用梯度方向直方图进行了旋转归一化HOG则主要面向行人检测这类任务它对局部形状和边缘信息敏感对光照变化有一定鲁棒性但对遮挡、形变很脆弱。虽然现在深度学习火了以后传统特征在大多数应用里已经被CNN特征替代但理解它们依然有价值。比如在做图像配准时SIFT特征的提取器和匹配器至今仍是baseline在特征点数量极端稀疏的场景如纹理很弱的物体表面你可以先跑一个SIFT特征试水再考虑上深度学习模型。笔试层面记住一句话就够了传统特征的精髓在于“手工设计的不变性”深度学习特征的精髓在于“端到端学习到的任务相关性”两者并不是完全冲突的关系。6. 备考路线与常见误区我给实习生的心里话最后这部分我想跳出具体题目聊聊备考策略和心态。很多人复习校招笔试时最容易范的一个错误是刷题只刷“看起来会考”的把整个知识面搞得又窄又碎。尤其像“计算机视觉算法实习生”这种岗位笔试考察面比你想的宽得多但深度又比你想的浅得多。准备时要广撒网把基础知识点都覆盖到但不需要在某个偏题怪题上死磕太久。6.1 三个月备考时间线参考如果今天距离笔试还有三个月可以参考这个节奏来安排第1个月算法与数据结构筑基。每天固定刷2-3道LeetCode或牛客题重点覆盖数组、字符串、链表、二叉树、动态规划、贪心。我建议一定要按专题刷不要随机刷否则知识框架建不起来。第2个月机器学习与深度学习基础。把西瓜书《机器学习》前七章和花书《深度学习》CNN相关章节过一遍。不用啃公式推导但概念、优缺点、公式含义一定要搞清楚。配合一些面试题库每天做20道选择题巩固。第3个月图像处理专项 限时模拟。用《数字图像处理》冈萨雷斯的前四章打底加上历年大厂笔试模拟题每周定时2小时做一套完整试卷练手感和时间分配。6.2 最容易踩的四个坑我在带学弟学妹过程中发现大家在备考CV岗笔试时普遍会踩这几个坑专门列出来给你提个醒。第一个坑是只刷题不复习基础LeetCode能AC三百题结果问你SVM的核函数怎么选、BN是怎么计算均值和方差的完全答不上来。第二个坑是轻视客观题总觉得编程题会写就稳了结果前面选择题错一半总分照样不够线。第三个坑是编程题不做现场模拟平时慢慢刷能AC一到限时环境就慌代码写不完整所以考前一定要用OJ模式掐表练习。第四个坑是忽略简历和笔试的匹配度笔试里的深度学习考点往往偏向你的项目方向简历上写了检测、分割却连yolo的anchor机制都说不清楚面试官怎么会信你真的做过。6.3 笔试之后的快速复盘方法考完试千万不要对完答案就完事。我建议你不管有没有进入下一轮都把这次笔试题目里不会的知识点整理到自己的错题本里。哪怕只记得题干的一个关键词也去查清楚背后的原理。往年的题很大程度上会重复出现核心考点比如KMP的next计算、感受野计算、动态规划优化今年复习到就是赚到。还有一点如果有机会的话可以回头把正确的代码再写一遍尤其那些当时没AC的题写不出来说明这个知识点还有盲区留在心里就是个隐患。写在最后一次笔试其实是一次浓缩的学习体检回到这套2018年的网易计算机视觉算法实习生笔试题它本质上是一次知识体检把数据结构功底、机器学习理解、图像处理基础、编码能力全部浓缩在两小时的考试里。我见过算法功底很好但机器学习基础薄弱的人被刷掉也见过机器学习理论很强但代码写不利索的人笔试挂掉。所以如果你现在还在准备阶段我的建议是不要只抱着“刷题通过考试”的心态而是借着备考把整个体系补扎实。这些底子不仅为了笔试更是你入职后写训练pipeline、调模型、做性能优化的基本功早一天补上后面就少一天手忙脚乱。
返回列表