我一直觉得,数据挖掘岗的笔试是互联网公司里最有“性价比”的一类题——它不像算法岗那样动不动手撕红黑树,也不像纯数据分析岗那样只考SQL和AB实验,而是把数学、代码、业务理解三件事揉在一张卷子里。
360的2016年数据挖掘笔试题,我印象很深。那年互联网公司校招的数据岗命题普遍开始从“考知识点”转向“考思维能力”,360的卷子在这方面很有代表性:题量不算大,但每道题都能看出出题人想考察什么底层能力。网上流传的版本和回忆版细节参差不齐,但这篇文章不是要给你罗列“原题答案”,而是结合那类题型的考察逻辑,把数据挖掘笔试最核心的知识点、推导过程、代码实现和避坑经验完整复盘一遍。适合准备互联网公司数据挖掘、机器学习、风控算法岗笔试的同学,也适合刚入行想系统补基本功的人。
1. 先从试卷结构说起:360数据挖掘笔试到底在考什么
1.1 一张笔试卷子的“三层意图”
很多人拿到笔试题的第一反应是“这题我见过,但不会做”,或者“这题很简单,但我答不全”。这其实不是知识点的问题,而是没有理解出题人想要什么。
360的笔试有一个很典型的特征:题目往往挂在具体业务场景上,但内核是通用基本功。比如它不会直接问你“什么是逻辑回归”,而是给你一个反作弊场景,让你判断「某流量被模型判定为作弊,它真的是作弊的概率是多少」。这就是明知故考:表面考贝叶斯公式,实际考你对“先验概率、似然概率、后验概率”这三个概念是否能灵活运用,并且是否知道在真实业务中误报率对结论的影响有多大。
我把这类笔试题的考察意图拆成三层:
- 第一层:基础知识层。机器学习算法原理、概率统计、特征工程基础、数据结构入门。这一层决定你能不能过线。
- 第二层:工具使用层。SQL取数、Python数据处理、简单的算法手撕。这一层决定你入职后能不能直接干活。
- 第三层:业务思维层。开放题、场景题,通常没有标准答案,考察你面对一个模糊问题时,能不能结构化拆解并给出可执行的方案。这一层是拉分项。
三层不是孤立的。一个候选人如果只在第一层很强,笔试分数可能还不错,但在面试聊开放题时很容易露馅。反过来,只会聊业务但没手算过硬核概率题的人,也会在第一轮就被筛掉。所以我建议准备笔试时,不要只刷题,要按这三层去搭建自己的知识体系。
1.2 重点考察方向与命题规律
整理2016年这波回忆版题目时,我发现频率最高的几类考点特別明确,也基本沿用了后面几年互联网公司数据岗笔试的命题主线。
| 考察模块 | 常见题型 | 难度 | 大致占比 |
|---|---|---|---|
| 机器学习基础 | 分类/聚类算法原理、过拟合与正则化、模型评估指标 | 中等 | 30% |
| 概率与统计 | 贝叶斯、全概率公式、期望与方差、简单假设检验 | 中等偏上 | 20% |
| SQL与数据处理 | 留存计算、PV/UV、分组TopN、窗口函数 | 入门到中等 | 20% |
| 编程与算法手撕 | 手写逻辑回归梯度下降、字符串处理、二叉树遍历 | 中等 | 15% |
| 开放业务题 | 反作弊、用户画像、推荐冷启动、流量异常检测 | 难 | 15% |
这个比例放到现在依然有参考价值。它说明一个事实:数据挖掘岗笔试不是纯粹的算法竞赛,而是“数学 + 工具 + 业务”的综合体检。如果你的目标公司是安全、搜索、广告这类有强业务和数据闭环的部门,开放题占比还会更高。
2. 核心考点深度拆解:算法原理比背结论重要
2.1 必考算法背后的“共同逻辑”
笔试里机器学习的题,很多人靠背结论应付,比如“逻辑回归适合二分类”“SVM用核函数处理线性不可分”“KMeans要选K”。但如果题目换一个场景,换个数据形态,背结论的人就会原形毕露。我建议从“它们到底在解决什么问题”的角度去理解。
以逻辑回归为例。它是数据挖掘笔试的第一高频考点,理由很简单:它既是线性模型,又是分类模型,还能解释特征重要性,而且和深度学习的最初几层有数学上的承接关系。笔试一般从三个角度出题:
- 损失函数为什么是交叉熵而不是均方误差?这个问题考的是对“梯度消失”和“凸优化”的理解。用MSE做二分类的损失函数,sigmoid的输出端梯度会很小,收敛极慢;而交叉熵配合sigmoid导出的梯度形式上恰好是$(y - p) \cdot x$,这个残差形式让模型更新的幅度和误差大小成正比,训练效率高得多。
- 为什么要做特征归一化?因为逻辑回归用梯度下降求解时,如果不同特征的量纲差异大,损失函数的等高线会变得很扁,梯度方向来回震荡,收敛很慢。这不是逻辑回归特有的问题,所有基于梯度优化的模型都需要考虑。
- L1和L2正则化有什么区别?L1会把不重要的特征权重压到0,起到特征选择的作用;L2会把权重整体压缩到接近0但不等于0。原因是L1在0点处不可导,优化过程中更容易让权重走到0。
再看树模型。决策树、随机森林、GBDT、XGBoost这套家族,笔试考的核心不是某个实现细节,而是“信息增益怎么算”“树模型为什么对特征尺度不敏感”“GBDT为什么用负梯度拟合残差”。信息增益考的是计算能力,手算一遍你才知道为什么连续特征要排序后找切分点;对特征尺度不敏感考的是原理理解,因为树的分裂只和特征取值的大小比较有关,和绝对数值无关;负梯度拟合残差则是把梯度下降思想推广到函数空间的体现——这也是让你把GBDT和逻辑回归联系起来的点。
KMeans考得相对简单,但容易踩坑的问题包括“初始中心怎么选”“K怎么确定”“欧氏距离在什么情况下不适用”。有一年我看到讨论题里有人直接把KMeans用在用户ID这种无意义离散特征上,这属于完全没有特征工程意识。聚类算法依赖距离度量,而距离度量的前提是特征在数值上有可比性、有实际含义。
2.2 概率统计:题目不难,错得可惜
数据挖掘笔试里最让人难受的其实是概率统计题——它不像机器学习题那样可以写一堆“我认为”,错了就是错了;也不像SQL题那样可以边写边试,纸面上推错了就是零分。
第一种高频题是条件概率与贝叶斯公式。互联网公司特别爱出这类题,因为它能直接对应业务里的“检测系统”:比如病毒查杀、流量反作弊、内容审核,都存在“检测结果是阳性但实际是误报”的情况。要算“检测为阳性且确实为真”的概率,贝叶斯公式几乎是标准解。关键不在于记住公式,而在于能把题目里的数字正确对应到P(A)、P(B|A)、P(B|¬A)上。我见过很多人在这一步出错,把“误报率”直接当成了“检测为阴性但实际是阳性”的概率,整个计算就全错了。
第二种高频题是期望和方差的计算。这个题型通常不复杂,但很考验细心,常常是“一个游戏有3个奖励等级,概率分别是0.5、0.3、0.2,对应奖金是100、200、1000,求期望收益和方差”。期望就是加权平均,方差就是偏离程度的平方再加权,只要分得清总体方差和样本方差的区别,一般不会丢分。
第三种是简单假设检验,比如给出一批样本均值,让你判断产品改动后指标是否显著提升。这类题考察的是p值、显著性水平、置信区间这些概念。笔试不会让你做完整推导,但会问“p < 0.05说明什么”,很多人的回答是“说明原假设成立的概率小于5%”,这是错的——p值描述的是“在原假设为真的前提下,观察到当前或更极端结果的可能性”,它不等于“原假设为假的概率”。这类概念辨析题,区分度极高。
概率统计这块没有捷径,我的建议是:把《概率论与数理统计》教材里的课后题挑着做一遍,尤其贝叶斯公式和全概率公式相关章节,然后去Kaggle或公司笔试题库里找“业务包装版”的概率题练习。基础公式要熟到能默写,因为在笔试现场没有时间让你现推。
2.3 特征工程:笔试中容易被低估的“软实力”
有笔试题目是这样的:“给你100万条文本数据,每条数据是一段URL,目标是识别恶意URL,你怎么设计特征?”这种题看似是开放题,其实是特征工程的基础能力测试。很多人上来就谈深度学习、谈模型选型,却忘了最基础的URL长度、域名年龄、是否有可疑关键词、DNS解析信息、页面内容相似度等特征。出题人想看的不是你会不会用多高端的模型,而是你有没有建立特征体系的思维框架。
特征工程的核心思路其实就四条:从原始数据里提取信息、把非结构化数据转成结构化表示、处理缺失值和异常值、选择有区分度的特征。
具体到URL反作弊这个场景,至少可以拆出几类特征:
- 基础统计特征:URL长度、路径层级数、参数个数、域名中数字的比例。
- 文本语义特征:是否包含“login/verify/account”这类钓鱼关键词、字符熵是否异常、是否使用了IP直连代替域名。
- 行为特征:同一IP下是否出现大量不同URL、同一URL被访问的时间间隔是否均匀得像机器。
- 外部情报特征:域名注册时间、WHOIS信息、是否出现在已知恶意情报库。
在笔试里写这类题,不要只列特征名称,要让面试官看到你有“特征评估”的意识,比如提到“这些特征需要做缺失值处理,因为WHOIS数据对很多新注册域名是缺失的”或“要对URL长度做分箱而不是直接输入原始值,因为极端长度会拉偏统计分布”。这种细节比堆十个特征名字更能体现基本功。
3. 实操过程:从真题到答案的思路还原
3.1 一道经典概率题的手算现场
下面用一个高度贴近当年题型风格的概率题来还原笔试现场的计算过程。
题目背景:某安全产品上线了一个恶意流量检测模型。根据历史数据,在所有流量中有5%是真正的恶意流量。模型对恶意流量的检测准确率(召回率)是95%,而对正常流量有3%的概率会被误判为恶意(误报率)。现在有一条流量被模型判定为恶意,问它真的是恶意的概率是多少?
先定义事件:
- A = 流量确实是恶意的。
- B = 模型判定为恶意。
已知:
- P(A) = 0.05,所以 P(¬A) = 0.95。
- 召回率 P(B | A) = 0.95。
- 误报率 P(B | ¬A) = 0.03。
要求的是 P(A | B)。
用贝叶斯公式:
$$ P(A|B) = \frac{P(B|A) \cdot P(A)}{P(B|A) \cdot P(A) + P(B|\neg A) \cdot P(\neg A)} $$
代入数字:
$$ P(A|B) = \frac{0.95 \times 0.05}{0.95 \times 0.05 + 0.03 \times 0.95} = \frac{0.0475}{0.0475 + 0.0285} = \frac{0.0475}{0.076} \approx 0.625 $$
结果是约62.5%。
这个数字是不是比大多数人直觉的“95%”低太多?问题就出在“基础比例”上。因为恶意流量本身只占5%,哪怕误报率只有3%,由于正常流量的基数太大,被误判出来的“假阳性”数量依然会和被正确检出的“真阳性”差不多,所以最后的后验概率只有六成左右。这个结论放到业务里非常重要:如果模型判定一条流量为恶意,安全团队直接封禁,那就有接近四成的概率误伤正常用户。
这道题在笔试里的满分答案不能只写公式和结果,最好补一句“在真实业务中,如果要降低误伤,可以调整阈值、引入更多证据、或提高先验概率的估计准确性”。这就把一道数学题升华成了业务题。
3.2 一道SQL题:新用户次日留存怎么算
SQL题在数据挖掘笔试里基本是送分题,但也是很多人丢分的题。原因倒不是不会写,而是细节处理错误,比如忘记去重、没有处理分区、日期函数用错。
最常见的题型是“给定用户登录日志表user_login,字段为uid、login_date,求2024年1月1日新增用户在其后第2天的留存率”(注意,有的题说“次日留存”指的是第2天,有的题把当天算作第1天,要仔细审题)。
按“当天注册算作第0天,次日即第2天”的常见口径来写:
WITH first_login AS ( SELECT uid, MIN(login_date) AS first_date FROM user_login GROUP BY uid ), new_users AS ( SELECT uid, first_date FROM first_login WHERE first_date = '2024-01-01' ) SELECT COUNT(DISTINCT n.uid) AS new_user_cnt, COUNT(DISTINCT CASE WHEN u.login_date = DATE_ADD(n.first_date, INTERVAL 1 DAY) THEN n.uid END) AS retained_cnt, COUNT(DISTINCT CASE WHEN u.login_date = DATE_ADD(n.first_date, INTERVAL 1 DAY) THEN n.uid END) * 1.0 / COUNT(DISTINCT n.uid) AS retention_rate FROM new_users n LEFT JOIN user_login u ON n.uid = u.uid AND u.login_date = DATE_ADD(n.first_date, INTERVAL 1 DAY);这道题有几个关键点:
- 先通过MIN(login_date)找到每个用户的首次登录日期,这是“新增用户”的定义。很多人直接对全表去重然后筛选那一天,会漏掉“该用户在1月1日之前已经登录过”的情况。
- 留存率计算需要从new_users表出发,LEFT JOIN登录表。如果用INNER JOIN,会把没有次日登录的用户过滤掉,留存率永远是100%,这是新手最容易犯的错。
- 分母要记得去重。一个用户同一天登录多次是常事,COUNT(DISTINCT uid)能避免重复计数。
- 日期函数不同数据库写法不一样,MySQL用DATE_ADD,Hive / Spark SQL可以用DATE_ADD或date_add,笔试时如果没注明环境,写出DATE_ADD这类标准函数一般没问题。
3.3 手撕代码:写一个简化版逻辑回归
有些公司的笔试会要求直接手写代码。2016年那会Python已经逐渐成为数据岗的主流语言,题目通常是“用numpy实现逻辑回归的训练过程”。
一个及格版本可以参考下面这段:
import numpy as np def sigmoid(z): # 防止溢出,把z裁剪到合理范围 z = np.clip(z, -500, 500) return 1.0 / (1.0 + np.exp(-z)) def train_logistic_regression(X, y, lr=0.1, num_iters=1000, lambda_reg=0.01): n_samples, n_features = X.shape w = np.zeros(n_features) b = 0.0 for i in range(num_iters): z = np.dot(X, w) + b pred = sigmoid(z) dw = np.dot(X.T, (pred - y)) / n_samples + lambda_reg * w db = np.sum(pred - y) / n_samples w -= lr * dw b -= lr * db if i % 100 == 0: loss = -np.mean(y * np.log(pred + 1e-12) + (1 - y) * np.log(1 - pred + 1e-12)) print(f"iter {i}, loss {loss:.4f}") return w, b这段代码有几个值得说的地方:
- sigmoid里做np.clip是为了防止np.exp(-z)溢出,这在真实数据里很常见,尤其是z值很大时。笔试题不会用太大数据,但写出这一步说明你踩过数值稳定的坑。
- 损失函数里加1e-12是为了防止log(0)出现NaN,属于经典防御性写法。
- 参数更新用梯度下降,梯度dw = X^T (pred - y) / n + λw,加了L2正则化项。很多答案只写dw = np.dot(X.T, (pred - y)) / n,正则化是加分项。
- 输出中间loss可以帮助调试,笔试时也方便展示你的训练过程。
如果笔试允许用sklearn,其实一行就能搞定,但手写代码题考的就是你能不能自己实现核心逻辑。建议在本地把这段代码跑通,再跑一个手写数字识别之类的二分类例子,加深“梯度更新是让loss下降”的体感。
3.4 开放式题:如何设计一个注册环节的风控体系
开放题是整套笔试题里最没有标准答案、但最考察“业务嗅觉”的部分。360的业务里,账户安全、反作弊、内容安全都是典型场景,所以开放题很可能是“新用户注册场景下的恶意注册识别方案”。
我的答题框架一般是三步:明确问题、拆解数据、设计策略。
第一步明确问题:恶意注册的定义要清晰,比如“同一设备短时间内注册大量账号”“使用虚假手机号批量注册”“注册后立即进行垃圾广告行为”,这些都是恶意注册的典型表现。
第二步从数据侧拆解:注册环节能拿到什么数据?设备信息(IMEI、MAC、IP、User-Agent)、手机号(号段、是否虚拟运营商)、注册时间、行为序列(是否滑动验证码、输入速度)、地理位置等。如果能拿到更长期的数据,还可以看注册后的行为,比如是否在短时间内添加大量好友、发布重复内容。
第三步设计策略:先讲特征,再讲模型,再讲人工兜底。模型上可以选用梯度提升树或逻辑回归做二分类,同时用规则引擎拦截高风险请求。这里记得提“样本标注”问题——恶意注册通常是极小众的,所以正负样本不均衡,需要做欠采样/过采样或使用AUC、召回率等指标而不是准确率。
还有一点很加分:提“策略闭环”。比如“模型识别出高风险用户后,进入人工审核队列;审核结果回流到训练集,定期迭代模型”。这表示你懂业务系统的运营逻辑,而不是只会在离线环境里调模型。
4. 常见问题与排查技巧实录
4.1 笔试现场的时间分配与做题顺序
数据挖掘笔试题通常给2小时左右,题量看着不多,但写起来才发现每道题都“烧脑”。我在实际笔试和模拟笔试里测试过,比较合理的时间分配是:
- 前10分钟:快速浏览全部题目,标注哪些题是自己熟悉的、哪些题需要想一想、哪些题暂时没思路。
- 30分钟:做概率统计和SQL题,这类题只要会就能拿满分,属于性价比最高的一类,一定要先拿下。
- 40分钟:做机器学习简答题和代码题,这类题需要组织语言,容易写着写着超时,所以要预留较长时间。
- 30分钟:做开放题,虽然不一定写得出完美答案,但尽量把框架搭完整,让阅卷人看到你的思考过程。
- 最后10分钟:检查有没有低级错误,比如公式带错数字、SQL忘记去重、计算结果没有写单位/概率。
我自己踩过的坑是:一开始死磕某道开放题,想把方案写得特别完美,结果前面简单的SQL题反而没时间写完整。后来我调整策略,开放题只写提纲而不是写论文,反而分数更高。
4.2 刷题阶段的高频错误与避坑
准备笔试过程中,有几个错误是高频率反复出现的:
- 背公式但不理解适用条件。比如一看到“检测阳性求真实概率”就动笔写贝叶斯,却没有验证题目给的“准确率”究竟是精确率还是召回率,导致代入的P(B|A)选错。
- 把留存率分母搞混。第N日留存率的分母是“第0日新增用户数”,分子是“其中在N日后仍然活跃的用户数”。很多人在多日留存题里,用“每日活跃用户数”做分母,最后算出来的结果完全没意义。
- 写SQL时不注意去重。这一点前面提到过,但值得再强调。日志表通常一行一条记录,同一个用户一天可能有多条,忘了DISTINCT,结果会偏差很大。
- 手写代码不检查边界条件。比如逻辑回归里没有clip、没有处理除零、没有正则化;写Python时只用list不用numpy导致运行超时。这些细节一眼就能看出有没有真实训练过模型。
- 开放题只给方案不给权衡。比如一上来就说“用深度学习模型识别恶意流量”,却不提误报率、样本量、训练成本、可解释性这些现实问题。好的答案应该展示“我会用XX方法,因为在这里误报的影响比较大,所以我会在阈值选择上偏向提高精确率”。
4.3 从笔试到面试:答题思路怎么延续
笔试不是终点,尤其是一线互联网公司,笔试成绩通常会和面试一起综合评估。很多时候,面试官手里就拿着你的笔试答卷,针对某个答案追问。
所以我的建议是:笔试时不要只写结论,要把推导过程尽量写出来。比如贝叶斯那题,虽然只要代入公式就能算出结果,但如果你把“A、B事件的定义”“先验概率、似然概率、误报率分别对应哪个数字”都写清楚,面试官一眼就能看出你是真懂还是背答案。到面试时,你还能围绕这道题补充“实际业务里如果我们把阈值调高,误报率会下降但召回率也会下降,这时候要看业务成本怎么权衡”,整个人的专业度立刻不一样。
开放题更是如此。笔试时写的框架,面试官可能会追问“你提到的特征具体怎么构造”“数据不均衡你怎么处理”“模型上线后怎么监控”。这些追问其实是好事,说明面试官对你的思路有兴趣。如果笔试时只是随便写了几个名词而没有深入思考,到面试环节就很容易被问穿。
5. 我从这套题里看出的“隐藏考点”
其实复盘到最后,你会发现一件事:笔试真正要筛选的,并不是“谁刷的题多”,而是“谁在遇到一个陌生的数据问题时,能快速形成一套从定义、拆解、建模到评估的完整思路”。
360这套2016年的题目,放在今天看可能有一些知识点已经更新了,比如深度学习相关的内容明显变多、大模型也开始进入一些笔试题。但底层的考察思路没变:数据挖掘岗要的不是会调包的人,而是懂数学原理、会处理数据、能理解业务的人。
举个很典型的例子,同样是考模型评估,低级答法是背出“精确率、召回率、F1、AUC”的定义;高级答法则会说“在恶意流量检测里,我更关注召回率,因为漏掉一条恶意流量的代价远高于误报一条正常流量,但也要考虑误报对用户体验的伤害,所以我会用AUC和PR曲线一起评估,再结合业务成本选阈值”。
这种回答的差距不是靠考前突击能拉开的,而是日常做项目、看数据分析报告、思考“这个业务指标为什么涨/跌”积累出来的。
所以我给准备数据挖掘岗笔试的朋友两个建议。
第一个建议:把刷题和复盘放在同等重要的位置。每做完一套题,不要只对答案,要把每道错题背后的知识点列出来,比如“这题考的是条件概率”“这题考的是窗口函数”“这题考的是特征归一化”,再针对薄弱点做专项训练。第二个建议:留出时间做至少一个端到端的实战项目,不管是Kaggle比赛还是自己找一个业务问题建模型,让整个流程从数据清洗、特征工程、模型训练到结果评估都亲自跑一遍。这个过程比刷二十套题都有效。
另外说一个我后来带团队时发现的现象:很多简历上写着“熟悉逻辑回归、决策树、XGBoost”的候选人,一问他“如果你的特征和标签之间有非线性关系,逻辑回归还适用吗”“XGBoost里的正则化参数干了什么”,就说不清楚了。这说明学习还是停留在调用层面,没有进入原理层面。笔试只是第一关,过了笔试还有更长久的成长问题。数据挖掘这个岗位,真正值钱的从来不是会哪个算法,而是能在合适的场景用合适的工具解决合适的问题。
最后分享一个小技巧,是我自己笔试和面试都在用的:面对一道不熟悉的题,先把题目改写一遍,用自己的话说清楚“题目给了什么、要我求什么、约束条件是什么”。这样做有三个好处——防止看错题、帮自己理清思路、让阅卷人看到你有分析框架。很多题看似复杂,只要把已知条件和目标写清楚,解决方案就会自动浮出水面。这套方法,屡试不爽。