下面进行华为2026年6月12日AI岗机考题的复盘和关键知识点(重点)深度剖析,笔者7月12-13日刷完选择题和2道算法题。选择题考到激活函数、神经网络"神经元死亡"、贝叶斯定理、随机变量、智能体长短期记忆、无监督学习、Transformer Block计算复杂度、训练优化器、KV Cache推理时存储压缩、量化压缩、RNN基础知识、Transformer基础知识、计算误差等知识点。
编程题有神经网络和传统的数据结构与算法的考点,神经网络题目考察多输出回归任务场景下的双层MLP(单次)前向传播和反向梯度计算(评级中等),传统考点依旧考察模拟,基于数据库ADD和QUERY指令的一元线性回归动态增量参数估计和预测(评级中等)。
说明:文章内选择题证明和论述部分的绿色字体是AI生成内容,请谨慎阅读并批判采纳;红色字体是人工增添或重要修改内容。
目录
[(1)KV Cache压缩原理与流程](#(1)KV Cache压缩原理与流程)
[1.1 KV Cache主流压缩方法](#1.1 KV Cache主流压缩方法)
[1.2 H2O算法的压缩流程](#1.2 H2O算法的压缩流程)
[3.1 矩阵条件数](#3.1 矩阵条件数)
[3.2 正交多项式基底的具体例子](#3.2 正交多项式基底的具体例子)
[3.3 关于正则化(Ridge Regression)的操作细节](#3.3 关于正则化(Ridge Regression)的操作细节)
[3.4 使数值稳定性变为1](#3.4 使数值稳定性变为1)
[3.5 预测每个token的未来Attenion Score趋势](#3.5 预测每个token的未来Attenion Score趋势)
[1. LSTM的场景(标准RNN / 门控路径)](#1. LSTM的场景(标准RNN / 门控路径))
[2. LSTM的核心设计(细胞状态路径)](#2. LSTM的核心设计(细胞状态路径))
[3. 梯度消失/爆炸的真实原因](#3. 梯度消失/爆炸的真实原因)
选择题(108/150)
用时43分钟
第六题
深度学习基础架构,Transformer与RNN,正确答案A。

使用普通Dense Attention,Transformer单步解码时间复杂度是O(n),使用线性注意力可以将单步解码时间复杂度降至O(1),这导致在并行算力资源受限的情况下,Transformer和RNN差距不大;但如果考虑模型参数体量和显存受限的场景,在对模型推理精度要求不高的情形下,RNN是更佳的选择。
第八题
大模型推理KV Cache压缩技术,正确答案D。

其实关于这道题有很多牵扯到的知识点,笔者想要重点讲解一下。涉及到关于KV Cache压缩技术的常见方法(H2O、A2SF等)、最小二乘法拟合与范德蒙德矩阵、矩阵条件数与数值稳定性优化、工程处理中的计算与访存性能平衡这四个知识点。
(1)KV Cache压缩原理与流程
1.1 KV Cache主流压缩方法
当前主流的、无需训练(Training-free)的KV Cache压缩方法,其核心都是通过某种启发式规则(Heuristic)来评估每个Token的重要性,并据此进行驱逐(Eviction)。这些方法主要分为几类:
- 基于注意力分数的方法:这是最常见的一类,例如 H2O、A2SF、SnapKV等。它们会利用历史注意力分数来识别并保留"重头戏"(Heavy Hitters)Token。例如,H2O通过历史注意力分数标量累积来为Token打分。
- 基于Key本身特征的方法:例如,有研究(A Simple and Effective L2 Norm-Based Strategy for KV Cache Compression)发现,Key向量的L2范数(L2 Norm) 与其未来的注意力分数高度相关。因此,可以直接依据Key的L2范数来压缩缓存,而无需计算注意力分数。
- 基于预测的方法:在H2O、A2SF等方法的基础上,对注意力分数做趋势外推预测,使得token的舍弃更具有针对性、准确性和鲁棒性。例如:
- Expected Attention:通过建模未来Query的分布,来预测每个KV对在未来将获得的注意力分数。
- AttentionPredictor:将注意力模式的预测视为一个时空序列预测问题,并使用一个轻量级的卷积神经网络(CNN)来预测未来的注意力分数。
1.2 H2O算法的压缩流程
其核心在于一个观察:在推理过程中,只有一小部分token(被称为"重量级token",Heavy Hitters, H₂)贡献了绝大部分的注意力价值。这些H₂ token的累积注意力分数遵循幂律分布(Power-law distribution),少数token的总分会远高于其他token。这些token被定义为 Heavy Hitters (H₂)。
筛选"重量级token"的流程大致如下:
- 持续累积:在生成新token的过程中,模型会持续监控并累积每个历史token所获得的注意力分数。每次计算新的注意力向量时,都会将最新的分数加到对应历史token的累计总分上。
- 动态筛选:当KV Cache达到预设的内存上限,需要驱逐一些token时,系统会检查所有历史token的累积注意力分数。
- 保留"重量级":系统会保留那些累积分数最高的token,即"重量级token"。研究表明,保留约20% 的"重量级token"就能在保证模型性能的同时,显著节省内存。
- 兼顾"新鲜度":除了保留"重量级token",策略通常还会保留最近生成的一批token,因为它们在生成后续内容时通常也很重要。
而为了解决历史累加注意力分数的时序新旧问题,越近生成的token理应具有更高的注意力总得分,后来学者也做了相关的探索和调整。A2SF(Accumulative Attention Scoring with Forgetting Factor)就是其中一种尝试。它在累积时引入一个小于1的遗忘因子(Forgetting Factor如0.9)。每次累加前,先将所有旧token的累积分数乘以这个遗忘因子,相当于给旧分数"打折"。这样,越旧的分数被惩罚得越多,新token的分数就能更快地凸显出来。
(2)二次多项式分数拟合
从1.2可以发现,压缩一般不是逐token进行的,大模型推理一般分为两个阶段,在Prefill阶段,将用户输入和上下文信息连贯输入模型,类似训练时的Teacher Forcing;而在Decode阶段,模型采用自回归(Auto Regression)的方式逐个逐个生成token。只有在Decode阶段,KV Cache达到显存预警界限的时候才会启用压缩。和H2O、A2SF等压缩算法不同,有一些算法(如1.1中所述)记录一个token在最近一段时间步的各个累加注意力分数,然后做出外推预测。看到这里,相信大家就能和数理统计里面的回归分析联系起来了。这道题采用的是一元二项式回归,abc三个系数待定,时间步一般在30-40,比如32。过大的历史长度区间会导致条件数指数级增加。显然这个方程组没有精确解(这是一个超定方程组),只能使用最小二乘法进行预测,最小二乘法对于多元非线性回归的通用处理做法是最小化残差平方和也就是随机误差。因此对每个待定参数求偏导数列方程组并求解,而用线性代数的语言就是求解范德蒙德矩阵。
范德蒙德矩阵
(维度为
):

注意力分数的向量
可以写成
。
因为
通常大于 3(比如 32 个点拟合 3 个未知数),这是一个超定方程组(Overdetermined System),没有精确解。而最小二乘法的矩阵形式解就是:

这个公式的推导,正是通过对
求梯度并令其为零得来的(即
)。
💡 工程上的"小秘密"
在KV Cache压缩的实际C++/CUDA工程代码中,我们不会真的去调用复杂的矩阵求逆库来解这个 3x3 矩阵,因为那样太慢了。
由于
的行是固定的等差时间序列(比如
),
这一部分是完全恒定的!我们可以提前在编译期把结果算出来,变成一个固定的
的系数矩阵。在实际推理时,只需要用这组固定的系数点乘当前的32个历史分数
,就能瞬间得到
。
(3)条件数与预测置信度,能否提升置信度?
3.1 矩阵条件数
对于一个可逆矩阵
,其条件数
定义为矩阵范数
与其逆矩阵范数
的乘积:

注:条件数的具体数值依赖于所取的范数类型(如2-范数、无穷范数等)。在机器学习中最常用的是2-范数条件数,它等于矩阵 A
的最大奇异值(特征值)与最小奇异值(特征值)的比值。
条件数越小,置信度越高。条件数最小是1,也就是数值最稳定性的取值。
3.2 正交多项式基底的具体例子
在等间隔采样的离散点上(比如时间步 x = 1, 2, 3, ..., t),我们不会用原始的 1, x, x²,而是使用离散勒让德多项式(Discrete Legendre Polynomials)作为基底。
假设我们取最近 t 个点,并将其中心化,令 z = x - (t+1)/2,使得数据点关于 0 对称(例如 z = -15.5, -14.5, ..., 15.5)。那么前三阶正交基底 P_0, P_1, P_2 的具体形式为:
- 常数项(均值):

- 线性项(趋势):
(中心化后,自动与常数项正交) - 二次项(曲率):
(减去一个常数,确保
且
)
使用这个基底的神奇效果:
当你把拟合方程写成
时,设计矩阵(相当于新的范德蒙德矩阵)的列是两两正交的。此时,
直接变成了一个对角矩阵(非零元素只在对角线上)。由于各列独立,求解
互不干扰,数值稳定性更高。
3.3 关于正则化(Ridge Regression)的操作细节
工程上的具体操作正是:在求解之前,先对
矩阵的对角线元素加上一个
。
修正后的正规方程(岭回归估计)为:

- 其中
是单位矩阵(对于二次多项式,是 3x3 的单位阵)。 - 对角线加上
(如 0.01)后,原来可能接近 0 的特征值被"抬升"了。这相当于在损失函数 L
中加了一项惩罚项】
,强制系数不会因为数据微小抖动而变得巨大。 - 数值选择:对于归一化后的注意力分数(通常在 0~1 之间),
取 0.001 到 0.01 之间,可以在几乎不增加拟合偏差(Bias)的前提下,将条件数从
量级压低到
量级,大幅提升预测系数的置信度。
💡 工程上的最优选
在真正的 C++/CUDA 推理代码中,"正交多项式基底 + 极小正则化"通常是组合使用的最佳实践:
- 先用正交基底(如勒让德)消除列相关性,让条件数本征接近 1。
- 为了对抗极长尾的数值舍入误差,依旧在对角线加上一个极小的
(如
)作为"保险",确保矩阵绝对可逆且系数平滑。
3.4 使数值稳定性变为1
- 构造矩阵:取 z = -15.5, -14.5, ..., 15.5,代入
,得到
(32行3列)。 - 逐列归一化:计算每列的L2范数(模长),将每列的所有元素除以该范数,得到标准正交矩阵
。 - 求解系数:用归一化后的矩阵
去拟合注意力分数
,求解新的系数 a , b , cT
。
⚡ 最重要的工程捷径(数学魔法)
当你完成归一化后,
满足
(单位矩阵)。这带来了一个惊天动地的简化:
你不再需要求解线性方程组了!
在标准最小二乘法中,你需要解
。
但由于
,方程直接简化为:

也就是说,新的三个系数
,直接等于归一化后的矩阵
的转置,点乘当前的32个历史注意力分数向量
。
3.5 预测每个token的未来Attenion Score趋势
当你用归一化后的系数
去预测下一步(第33步,即
)的注意力分数时,务必使用归一化后的新基底:
- 先算出第33步的三个基底值:
。 - 对这三个值做同样的归一化处理(除以对应列在构造时算好的那个L2范数),得到
。 - 预测分数
。
数学上绝对保证,这样算出来的预测值,和用原始系数去算的原始方法得出的数值完全一模一样,没有任何精度损失。
(4)硬件加速单元吞吐量和词元总数量n的关系
吞吐量和词元数量,总体上是一个类似凹函数的先升后降的曲线。因为计算的大头是注意力机制,使用KV Cache的情况下,生成一个新的token时间复杂度大约是O(2nd),而对于固定的回归参数估计历史长度(比如t=32),只有在KV Cache存储占用达到预警线时才计算并压缩一次,而且每个token计算(参数估计和分数预测)的时间复杂度是O(t+1),总估计复杂度是O(n(t+1)),和而t+1<<2d,因此吞吐量线性增长和KV Cache压缩技术无关,是大模型自回归预测的必然结果。
📊 总结对比表
| 场景 | Token数量 (n) | 拟合开销 (每个Token) | 注意力计算总开销 | 硬件真实感受(延迟) |
|---|---|---|---|---|
| 内存预警初期 | 较小 (如 < 200) | 极低 (O(1)常数项) | 低 (O(n²) 数值小) | 延迟低,但受限于内存带宽 |
| 长序列生成后期 | 较大 (如 > 4000) | 恒定 (O(1)常数项) | 极高 (O(n²) 数值大) | 延迟高,计算单元满负荷但算不过来 |
第十题
类别不平衡的分类任务的损失函数设计,Focal Loss、BCE、Dice Loss等等。参考我之前写的一篇文章:深度学习与机器学习算法:损失函数设计-分类任务(1)-CSDN博客。正确答案B。

第十一题
智能体长短期记忆,正确答案A。

🧠 为什么A(结构化长期记忆 + 语义检索)是最合理的?
正是因为短期窗口存在上述天然的物理和计算缺陷,A选项直接从系统架构层面解决了问题,而不仅仅是"修补"窗口:
- 结构化存储:它将"网络延迟"从流动的对话文本中抽取出来,固化为一个独立的事实条目(用户常见问题)。这样就不再依赖原始文本在上下文中的位置。
- 语义检索(类RAG):这正好解决了你担心的"表述变异"问题。当用户后续说"卡顿"时,系统通过向量检索,发现"卡顿"的嵌入与长期记忆中"网络延迟"的嵌入在空间上距离很近,于是主动触发,将其拉回当前的短期上下文中。
❌ 为什么B、C、D都不如A?
- B(增大短期窗口/摘要):这是"治标不治本"。窗口再大也有极限,且摘要会丢失关键细节(比如具体的延迟发生时间点)。当对话拉长到100轮,B方案依然会失效。
- C(引入排障工具):工具是用来解决问题的,而不是用来识别重复问题的。用户还没指定要排障,模型如果先触发工具,逻辑混乱。它无法帮助模型回忆起"两个月前用户也提过这个事"。
- D(优化Prompt提示词):单纯的Prompt只能提高模型的注意力权重,但无法对抗上下文截断。如果"网络延迟"这个词已经被挤出窗口,提示词写得再好也没用。
第十六题
激活函数,正确答案ABCD。

这里重点讲一下任务类型和训练数据特点(输入和标签)的重要性,在计算机视觉3D重建、蛋白质序列到折叠结构预测这两个领域,因为涉及到基频信号、高频信号和谐振理论,使用周期和频率合适的三角函数作为激活函数是很合适的,在这两个场景可能优于现代大模型常用的GeLU、CNN经常使用的ReLU、LSTM使用的Sigmoid和Tanh等激活函数。
计算资源受限的情况下,结合实际场景选择ReLU这种计算简单的激活函数,像GeLU负实数定义域、Sigmoid、Tanh这种激活函数的非线性程度较高,计算起来较慢。
网络深度较深的情况下,一般选择梯度最大值大于等于1,最小值小于1,且梯度在1周围分布较为均匀的激活函数(ReLU、GeLU满足;Sigmoid导数
最大值0.25,大部分情况远小于1;Tanh导数
最大值1,大部分情况远小于1),在CNN和Transformer加入何恺明提出的Residual Block模块,避免梯度消失问题。
LSTM如何解决梯度消失问题&&处理梯度爆炸的业界手段
这边我顺便补充一下LSTM解决传统RNN梯度消失(比梯度爆炸更常见,梯度爆炸可以使用梯度裁剪解决,传统RNN梯度消失问题无法解决)的办法。传统RNN梯度消失问题与网络深度无关,是因为网络数据流依照时序排布,一旦输入序列过长,就很容易发生梯度消失。

1. LSTM的场景(标准RNN / 门控路径)
你算出的激活函数导数(都≤1)确实会导致梯度消失。在LSTM中,这部分导数存在于"门控单元"(输入门、遗忘门、输出门)的计算路径中。
- 关键事实:这些门控路径的梯度只在当前时间步内流动,它们不通过时间步长(t-1, t-2...)循环相乘。
- 因此,sigmoid/tanh导数小于1并不会导致跨时间步的梯度消失,它只影响当前步的参数更新幅度。
2. LSTM的核心设计(细胞状态路径)
LSTM为了解决梯度消失,专门构建了一条"高速路"------细胞状态(Cell State)
。误差反向传播时,最关键的计算是:

请注意:这个公式里完全没有激活函数的导数(如
或
)!
- 它直接依赖于遗忘门(Forget Gate)的激活值
(范围在0~1之间),而不是
的导数。
。
3. 梯度消失/爆炸的真实原因
- 梯度消失(在LSTM中):如果遗忘门
持续逼近 0(即模型学会了"忘记"所有历史信息),那么
,梯度就会消失。
- 工程解药:正因为这个原因,业界标准做法是将遗忘门的偏置(
)初始化为较大的正数(如1.0或2.0),使得初始
,强制网络进入"记住"状态,从而主动避免梯度消失。
- 工程解药:正因为这个原因,业界标准做法是将遗忘门的偏置(
- 梯度爆炸(在LSTM中):如果遗忘门
持续逼近 1,并且叠加的项很多,梯度可能会累积得很大,导致梯度爆炸。
- 工程解药:正因为存在爆炸风险,所有LSTM训练都必须配合梯度裁剪(Gradient Clipping)。
第十九题
无监督聚类和主成分分析,正确答案ACDE。

K-means适用于低维且意义明确的低维空间,而且很依赖特征工程。最终聚类结果和初始聚类中心的选择也相关,在一些容易球形聚类错误的场景中,K-means环节可以通过多次冷启动(重新选择初始簇中心并开始聚类,一般不是随机初始化簇中心,而是随机选择K个样本向量作为初始簇中心)得到平均的更鲁棒的聚类结果(类似Bagging策略)。
(1)DBSCAN的核心原理(过程导向而非结果导向)
DBSCAN的核心思想是:将"高密度"区域划分为一个簇,而"低密度"区域则被视为噪声。
算法围绕两个关键参数展开:
- ε (epsilon):扫描半径,定义了一个点的"邻域"范围。
- MinPts:形成一个"高密度"区域所需的最少点数。
基于这两个参数,DBSCAN将数据点分为三类:
- 核心点 (Core Point):在其 ε 邻域内,点的数量大于等于 MinPts。它是簇的"骨干",有足够多的邻居。
- 边界点 (Border Point):位于某个核心点的邻域内,但它自己邻域内的点数小于 MinPts。它是簇的边缘。
- 噪声点 (Noise Point):既不是核心点,也不在任何核心点的邻域内。它被算法视为"离群点"并自动忽略。
算法的执行流程如下:
- 随机探索:从数据集中任意选择一个尚未被处理的点。
- 检查密度:计算该点的 ε 邻域内有多少个点。
- 情况一:核心点(密度足够):如果邻域内的点数 >= MinPts,则创建一个新簇,并将该点及其邻域内的所有点都加入这个簇。
- 簇的"滚雪球"式扩张:接着,对新加入的每一个点,都重复执行步骤2。如果新加入的点也是核心点,就把它的邻域内的所有点也拉进这个簇里。这个"寻找邻居-拉入伙"的过程会不断递归,直到无法再扩张为止。
- 情况二:非核心点:如果邻域内的点数 < MinPts,则该点暂时被标记为噪声点。(注意,这个点之后可能会被某个核心点"收入麾下"成为边界点)。
- 循环直至结束:算法会继续选择下一个未处理的点,重复上述过程,直到数据集中的所有点都被访问过。
| 特性 | K-means | DBSCAN |
|---|---|---|
| 核心思想 | 基于距离,划分球形簇 | 基于密度,连接任意形状簇 |
| 主要参数 | K (簇的数量) | ε (半径) 和 MinPts (最少点数) |
| 簇的形状 | 假设为球形或凸形 | 可发现任意形状(如细长、环形等) |
| 对噪声处理 | 敏感,会将噪声点强行归入某个簇 | 鲁棒,能自动识别并忽略噪声点 |
| 簇的数量 | 需要预先指定 | 由算法自动推断 |
| 适用数据 | 大型、结构清晰、球形的数据集 | 形状不规则、密度不均、含噪声的数据集 |
(2)t-SNE原理
- 高维空间:欧氏距离 → 高斯概率
对于高维空间中的任意两个数据点
和
,t-SNE 会执行以下两步:
- 计算欧氏距离:先算出两点之间的直线距离
。 - 代入高斯分布(正态分布):将这个距离放入一个以 xi
为中心的高斯概率密度函数中,计算出"条件概率"
。

通俗解释:想象你在高维空间中的点
处放了一个热气球(高斯分布),气球里的气体密度随距离增加而下降。
就是"点
落在
这个气球笼罩范围的概率"。距离越近,概率越高;距离越远,概率指数级衰减。
- 至关重要的"归一化"与"困惑度"
- 归一化:分母是
到其他所有点的距离之和。所以,对于同一个
,所有点的
加起来等于 1。这表示:"在
的视角下,谁是我最亲密的邻居?" - 带宽调节(
):每个点
都有一个专属的
(高斯分布的宽度)。算法会通过二分搜索调整这个
,使得这个高斯分布覆盖的"有效邻居数"等于你设定的困惑度(Perplexity)。如果困惑度设为 30,算法就会自动把每个点的
调到"恰好能覆盖大约 30 个最近邻"的程度。
- 对称化(为了双向奔赴)
因为
和
可能不等,为了数学上更对称,t-SNE 最终取两者的平均值,得到最终的联合概率
:

(这确保了"我认为你是邻居"和"你认为我是邻居"被同等对待。)
⚠️ 关键盲区纠正:它不"判断类别"
你提到"判断不同向量点的类别",这一点需要特别澄清:
- 高维计算阶段:t-SNE 完全不知道哪些点属于"欺诈",哪些属于"正常"。它只是机械地计算所有点之间的几何邻近概率,完全不看标签。
- 可视化阶段:当我们把降维后的点画在 2D 图上时,是我们人类手动给这些点上了颜色(比如盗刷的点标红,正常的标蓝)。t-SNE 只是尽力把高维空间中概率高的邻居,在低维空间也放到一起,导致红点和蓝点自然分堆。
- 为什么低维空间不用高斯,而用 t 分布?
这是 t-SNE 最精妙的一步:
- 在高维空间,我们用高斯分布(窄尾巴)。
- 在低维空间(2D/3D),我们用t 分布(自由度=1,即柯西分布)(长尾巴)。
- 目的:如果高维空间两个点很远(相似度低),高斯分布给它们的概率是极小的(接近0),但在低维空间,t 分布的长尾巴会让它们之间的距离被夸大地拉远。这就是所谓的"长尾排斥力",它能把原本在原始空间粘连在一起的簇强行"撑开",形成你在图上看到的那个清晰的"岛屿"效果。
(3)PCA和t-SNE如何合作?真实场景怎么应用?
PCA是基于线性代数的降维方法,t-SNE是基于概率论的低维空间(2D或3D)的t分布放大概率差异和高维空间高斯概率分布计算结合的方法。我让DeepSeek举了一个防范金融盗刷案的例子:
假设原始数据有 100维特征(包含交易金额、商户类别、设备指纹、地理位置、交易时间间隔等)。
🚀 第一阶段:用 PCA 做"去噪"与"全局扫描"
目的:快速剔除"一眼假"的离群点,并为后续计算做准备。
- 降维与筛选:将100维数据用PCA降到 50维(保留95%以上的方差)。
- 绘制 PC1 vs PC2 散点图:
- 你会发现,99.9% 的点聚集在中心的一个椭圆区域(正常交易)。
- 但有 0.05% 的点,在PC1轴向上飞出去极远(比如PC1值 = 500,而正常范围是 -10 ~ 10)。
- 工程处置:
- 直接判定:这些极端离群点(如交易金额是普通用户的1000倍,且设备异常),大概率是明显的盗刷,直接标记为"强异常"送入人工审核。
- 过滤:将这几个极端点移除,剩下的数据作为"干净"的候选集,准备进行精细分析。
为什么必须先用PCA? 如果直接把这100维原始数据扔给t-SNE,这几个极端离群点会严重拉伸t-SNE的梯度空间,导致正常点全被挤在一个针尖大的区域,完全无法看清内部结构。
🔬 第二阶段:用 t-SNE 发现"隐藏的簇结构"
目的:针对滤除极端点后的数据,找出欺诈行为的不同"亚型"。
- 输入:将刚才 PCA 降维后的 50 维主成分得分,输入给 t-SNE,降维至 2D(困惑度设为 30)。
- 观察图像:
- 你会在二维平面上看到,正常的交易数据形成了一大片松散的、连绵的"大陆"。
- 但在这片大陆的边缘和缝隙里,出现了 3个极其紧凑的、孤立的小"岛屿"(簇 A、B、C)。
- 关键步骤(反向映射):
- 不要直接记录这些岛屿在t-SNE图上的坐标距离。
- 将 t-SNE 图上属于簇 A、B、C 的样本点 ID 提取出来,返回原始 100维特征空间,计算这三个簇的特征均值。
- 惊人发现:
- 簇 A:特征是"凌晨2点-5点" + "多笔连续小额支付"(疑似机器测试盗刷)。
- 簇 B:特征是"海外IP" + "大额虚拟商品"(疑似信用卡套现)。
- 簇 C:特征是"同一设备频繁更换账号"(疑似团伙攻击)。
⚙️ 第三阶段:异常检测的最终落地(摒弃t-SNE)
重要转折:现在我们已经知道了数据存在这3种特定的欺诈模式,但在生产环境中,我们绝不会部署t-SNE来做实时推理(因为它太慢且不稳定)。
我们会利用 t-SNE 的"勘探"结果,回到传统ML领域进行硬编码或建模:
- 规则生成:
- 针对簇A的特征均值,设置硬规则:IF 时间 BETWEEN 2:00-5:00 AND 金额 < 50 AND 连续交易次数 > 3 THEN 拦截。
- 针对性建模:
- 用簇A、B、C的样本作为正样本,随机抽取大量正常样本,训练一个轻量级的Isolation Forest(孤立森林)或 XGBoost。
- 此时,算法不需要面对海量杂乱的数据,只需要针对 t-SNE 找出的那几块"可疑区域"进行特征阈值划分,准招率(Precision/Recall)会极高。
💎 实战总结:PCA 与 t-SNE 的"接力棒"关系
| 阶段 | 工具 | 核心动作 | 工程产出 |
|---|---|---|---|
| 第1棒 | PCA | 压平全局,捡出飞得最远的点 | 剔除极端噪声,防止污染t-SNE,顺便白捡几个异常Case |
| 第2棒 | t-SNE | 展开局部流形,发现紧凑小簇 | 输出可疑样本的 ID 列表(注意:只用ID,不用坐标) |
| 第3棒 | PCA/原始特征 | 提取这些ID对应的原始特征均值 | 总结出"凌晨小额"、"海外大额"等可解释的业务规则 |
| 第4棒(生产) | XGBoost / 规则引擎 | 部署轻量级模型,不做t-SNE | 毫秒级响应,准确拦截 |
这就是工业界的标准玩法:t-SNE是"探矿机",用来发现矿脉(簇结构);发现矿脉后,扔掉探矿机,改用"挖掘机"(规则或传统ML模型)基于这些矿脉的位置进行精准开挖。
代码题1
在统计学习方法和数据库插入、查询(计算)复合场景下的模拟(一元线性回归,增量动态更新参数),通过率90%->调整一元线性回归截距的计算公式,通过率变为100%
用时较长,未记录
第2题-智能物流定价引擎(在线学习) - problem_ide - CodeFun2000


python
def code():
Q = int(input())
data = []
params = [0, 0]
results = []
sx, sy, sxx, sxy = 0, 0, 0, 0
def regress(data, params, orgl, sx, sy, sxx, sxy):
sx += sum([d[0] for d in data[orgl:]])
sy += sum([d[1] for d in data[orgl:]])
sxx += sum([d[0] ** 2 for d in data[orgl:]])
sxy += sum([d[0] * d[1] for d in data[orgl:]])
if len(data) * sxx != sx ** 2:
params[0] = (len(data) * sxy - sx * sy) / (len(data) * sxx - sx ** 2)
params[1] = (sy / len(data)) - (sx / len(data)) * (params[0])
else:
params[0] = 0
if len(data) > 0:
params[1] = (sy / len(data))
else:
params[1] = 0
return sx, sy, sxx, sxy
orgl = 0
for q in range(Q):
l = input().split()
if l[0] == "ADD":
x = int(l[1])
y = int(l[2])
data.append((x, y))
else:
sx, sy, sxx, sxy = regress(data, params, orgl, sx, sy, sxx, sxy)
x0 = int(l[1])
yp = params[0] * x0 + params[1]
results.append(yp)
orgl = len(data)
for r in results:
print("{:.6f}".format(r))
仅在检索到Query语句时更新参数并计算,如果Query时并未检测到新的数据插入则仍然使用原来的参数结果,但是计算过程照常进行(sx,sy,sxx,sxy增量为0)。
代码题2
多输出回归任务场景下的机器学习双层MLP(单次)前向传播和反向梯度计算,ReLU作为激活函数,全部AC,用时42分钟。
第3题-回归任务反向传播机理 - problem_ide - CodeFun2000

python
# 其实不需要使用np.float64进行类型转换
def code2():
Q = int(input())
data = []
params = [0, 0]
results = []
sx, sy, sxx, sxy = (0), (0), (0), (0)
def regress(data, params, orgl, sx, sy, sxx, sxy):
sx += sum([d[0] for d in data[orgl:]])
sy += sum([d[1] for d in data[orgl:]])
sxx += sum([d[0] ** 2 for d in data[orgl:]])
sxy += sum([d[0] * d[1] for d in data[orgl:]])
if len(data) * sxx != sx ** 2:
params[0] = (len(data) * sxy - sx * sy) / (len(data) * sxx - sx ** 2)
params[1] = ((sy) - (sx) * params[0]) / (len(data))
# 按照一元线性回归的最小二乘法法计算公式,截距计算更优的选择应该如下
# 但是用例5有几个Query结果第6位小数有1-5左右的偏差
# params[1] = (sy)/ (len(data)) - (sx) / (len(data)) * params[0]
else:
params[0] = 0
if len(data) > 0:
params[1] = (sy / len(data))
else:
params[1] = 0
return sx, sy, sxx, sxy
orgl = 0
for q in range(Q):
l = input().split()
if l[0] == "ADD":
x = int(l[1])
y = int(l[2])
data.append((x, y))
else:
# 仅在查询时增量更新,加速计算
sx, sy, sxx, sxy = regress(data, params, orgl, sx, sy, sxx, sxy)
x0 = int(l[1])
yp = params[0] * x0 + params[1]
results.append(yp)
orgl = len(data)
for r in results:
print("{:.6f}".format(r))