大厂真题 / 华为
华为 9.2 笔试真题 - 国内 AI 岗
本场考试概述
考试时间:2026 年 9 月 2 日
考试岗位:国内 AI 岗
证据边界:现有材料宣称本场包含 20 道选择题(15 道单选、5 道多选)和 2 道编程题,但正文实际只能恢复 12 道单选题、0 道多选题和 2 道编程题。本文只整理有正文依据的内容,不补写缺失的 8 道选择题,也不把概述中出现的考点反推成原题。
第 1 部分:可恢复单选题
以下答案均根据题干独立核验,而不是仅转录材料答案。
1. 可逆矩阵
矩阵逆运算的前提是?
- A. 矩阵为长方形
- B. 行列式为零
- C. 矩阵为方阵且满秩
- D. 列向量线性相关
独立核验答案:C。通常所说的双边逆要求矩阵为方阵;方阵可逆又等价于满秩、行列式非零和列向量线性无关。
2. 链式法则求梯度
对于单样本损失 $L=(wx+b-y)^2$,关于参数 $w$ 的梯度为?
- A. $x^2$
- B. $y-w$
- C. $2(wx+b-y)$
- D. $2(wx+b-y)x$
独立核验答案:D。由链式法则,
\[\frac{\partial L}{\partial w}=2(wx+b-y)\frac{\partial(wx+b-y)}{\partial w}=2(wx+b-y)x.\]3. 过拟合判断
Transformer 文本分类训练时,训练精度持续上升至 97%、训练 loss 持续下降;验证精度达到峰值后回落、验证 loss 持续上升。下列判断与应对方案中不合理的是?
- A. 模型出现欠拟合,需要增大模型容量
- B. 可适当增大权重衰减(L2 正则)缓解过拟合
- C. 模型出现过拟合,泛化能力下降
- D. 可早停于验证性能最优处
独立核验答案:A。训练表现继续改善而验证表现恶化是典型过拟合现象;增大容量通常不能解决这一问题。B、C、D 均与该判断相符。
4. Residual Add 与 RMSNorm 融合
Transformer Block 中 Residual Add 后通常紧接 RMSNorm,推理优化中常把二者融合为一个 Kernel,主要原因是?
- A. 硬件不支持加法
- B. 残差连接无法计算
- C. 避免残差结果写回 HBM,直接在片上完成归一化
- D. 为了增加 FLOPs
独立核验答案:C。融合可以减少中间张量的一次写回与再次读取,并减少 Kernel 启动开销;目的不是增加计算量。
5. Top-P 采样
Top-P(Nucleus Sampling)中参数 $p$ 代表什么?
- A. 抛弃概率低于 $p$ 的 Token
- B. 按概率降序,截取累计概率达到或刚超过 $p$ 的最小 Token 集,重新归一化并采样
- C. 从概率最高的前 $p$ 个 Token 采样
- D. 以 $p$ 的概率选择最高概率 Token
独立核验答案:B。更严格地说,应取按概率降序排列后的最小前缀,使其累计概率不小于 $p$,再在该集合中重新归一化采样。
6. 漏检敏感指标
若任务对漏检正样本的代价特别敏感,应优先关注哪个指标?
- A. Accuracy
- B. Precision
- C. Top-1 Accuracy
- D. Recall
独立核验答案:D。漏检对应假负例 $FN$,而 $Recall=TP/(TP+FN)$,因此召回率直接反映正样本被找回的比例。
7. 通用近似定理
通用近似定理主要说明什么?
- A. 增加深度一定比增加宽度好
- B. 使用线性激活且至少有一个隐藏层的网络可任意逼近任何连续函数
- C. 使用非线性激活(如 Sigmoid)且足够宽的单隐藏层网络可逼近任何连续函数
- D. 神经网络总能找到全局最优解
独立核验答案:C(按本题考试意图)。严格地说,“只要非线性”过于宽泛:不同版本的定理会要求激活函数满足非多项式、sigmoidal 等条件,并通常讨论紧集上的连续函数。选项给出的 Sigmoid 示例符合经典版本;定理说明的是表示能力,不保证训练能找到全局最优解。
8. 持续新数据下的 HAC 工程方案
持续到来的新数据下,为兼顾 HAC 的层次分析能力和系统效率,通常采用什么工程方案?
- A. 每来一个样本都对全量数据重做完整 HAC
- B. 把 HAC 改成监督学习模型
- C. 在线近似归组,离线用 HAC 重整或精修
- D. 放弃历史簇,只聚类最新样本
独立核验答案:C。它是四个选项中最合理的工程折中。需要注意,这是一项依赖延迟、内存与数据分布的工程判断,不是唯一算法定理;增量层次聚类、微簇等方案在特定条件下也可能适用。
9. 单链接定义
层次聚类中 single-linkage 的簇间距离定义为?
- A. 两簇最远样本点的距离
- B. 两簇最近样本点的距离
- C. 两簇中心点的距离
- D. 所有跨簇点对距离的平均
独立核验答案:B。单链接取两个簇之间所有跨簇点对距离的最小值。
10. PCA 主成分数
PCA 降维时通常依据什么指标选择保留的主成分数量?
- A. 特征值累计贡献率
- B. 数据均值
- C. 特征向量模长
- D. 投影样本方差倒数
独立核验答案:A。协方差矩阵的特征值对应各主成分解释的方差,实践中常选择使累计解释方差达到目标阈值的最少主成分。
11. Prefill 与 Decode 的典型瓶颈
LLM 推理服务中 prefill 与 decode 的典型瓶颈分别更接近哪种特征?
- A. 都只受网络带宽限制
- B. prefill 访存受限、decode 算力受限
- C. 都只受 CPU 线程限制
- D. prefill 算力受限、decode 内存或访存受限
独立核验答案:D。Prefill 通常包含算术强度较高的大矩阵运算,decode 单步则常需要为少量 token 读取大量权重和 KV Cache。该结论是 Roofline 意义下的典型概括,实际瓶颈仍会随 batch、序列长度、硬件、量化和调度方式变化。
12. Megatron-LM 张量并行通信
Megatron-LM 风格的张量并行(TP)中,通信操作(如 All-Reduce)主要发生在?
- A. 仅 Loss 计算阶段
- B. 每个 Transformer 层内部的前向和反向传播过程中
- C. 仅训练结束后的参数同步阶段
- D. 仅数据加载阶段
独立核验答案:B。张量被切分后,每层前向与反向路径都可能需要集合通信来组合局部结果。具体使用 All-Reduce、Reduce-Scatter 还是 All-Gather,以及每层通信次数,会随行并行、列并行和序列并行实现而变化,不能把某个固定次数视为普遍规律。
第 2 部分:编程题
第 1 题:滑动窗口注意力机制
题面
标准缩放点积注意力为
\[\operatorname{Attention}(Q,K,V)=\operatorname{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V.\]对于 batch 中每组长度为 $N=\text{seq_len}$、向量维度为 $d_k$ 的 $Q,K,V$,位置 $i$ 的 query 只关注满足 $\lvert i-j\rvert\le \omega$ 的 key 位置 $j$,其中 $\omega=\text{window_size}$;越过序列边界的部分自然截断。窗口内分数为
\[S_{ij}=\frac{Q_i\cdot K_j}{\sqrt{d_k}}.\]Softmax 只在允许位置上归一化。为保证数值稳定,对每行先减去窗口内最大分数 $m_i$:
\[A_{ij}=\frac{\exp(S_{ij}-m_i)}{\sum_{t:\,\lvert i-t\rvert\le\omega}\exp(S_{it}-m_i)}, \qquad O_i=\sum_{j:\,\lvert i-j\rvert\le\omega}A_{ij}V_j.\]窗口外权重为 0。这里的 window_size 按公式实际表示窗口半径,内部位置最多关注 $2\omega+1$ 个 token,并非窗口的总长度。
输入描述
第一行输入四个整数:
batch_size seq_len d_k window_size
随后输入 batch_size 组数据。每组依次包含:
- Q 的
seq_len行,每行d_k个浮点数; - K 的
seq_len行,每行d_k个浮点数; - V 的
seq_len行,每行d_k个浮点数。
输出描述
每组输出 seq_len 行,每行 d_k 个浮点数并保留 2 位小数。相邻两组结果之间输出一个空行,最后一组之后不输出额外空行。
若首行参数违反约束,直接输出一行 0,不再读取矩阵。
约束
\[\text{batch\_size}\ge1,\quad \text{seq\_len}\ge1,\quad d_k\ge1, \quad 1\le\text{window\_size}\le\text{seq\_len}.\]材料没有进一步定义浮点结果恰在两位小数中点时的舍入方式。本文约定输出采用 round half away from zero(恰好一半时向远离 0 的方向舍入),且把 -0.00 规范化为 0.00。
样例 1
输入
1 4 4 1
0.1 0.2 0.3 0.4
0.2 0.3 0.4 0.5
0.3 0.4 0.5 0.6
0.4 0.5 0.6 0.7
0.1 0.0 0.1 0.0
0.0 0.1 0.0 0.1
0.1 0.1 0.0 0.0
0.0 0.0 0.1 0.1
1.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0
0.0 0.0 1.0 0.0
0.0 0.0 0.0 1.0
输出
0.50 0.50 0.00 0.00
0.33 0.34 0.33 0.00
0.00 0.33 0.33 0.34
0.00 0.00 0.50 0.50
样例 2
输入
1 2 2 0
0.3 0.4
0.4 0.5
0.5 0.6
0.6 0.7
0.1 0.0
0.0 0.1
输出
0
window_size=0 不满足题目给定范围,因此走非法参数分支。
算法
对每个 batch、每个 query 位置 $i$:
- 计算窗口端点 $l=\max(0,i-\omega)$、$r=\min(N,i+\omega+1)$;
- 只计算 $j\in[l,r)$ 对应的缩放点积分数,不构造 $N\times N$ 分数矩阵;
- 从所有分数中减去最大值,再求指数和,得到稳定的窗口内 Softmax;
- 用这些权重对对应的 $V_j$ 加权求和。
输出格式化只发生在全部实数计算完成之后,不能先把注意力权重截成两位小数再参与加权。
正确性证明
对任意 batch 和位置 $i$,算法计算的区间
\[[\max(0,i-\omega),\min(N,i+\omega+1))\]恰好包含且仅包含所有满足 $0\le j<N$ 与 $\lvert i-j\rvert\le\omega$ 的位置,所以打分集合与题面要求完全相同。
算法对该集合中的每个 $j$ 计算题定分数 $S_{ij}$。所有分数同时减去 $m_i$ 不改变 Softmax,因为分子、分母都乘以相同常数 $e^{-m_i}$;因此算出的权重就是题定 $A_{ij}$,窗口外位置没有进入分子或分母,其权重等价于 0。最后逐维计算 $\sum_jA_{ij}V_j$,正好得到 $O_i$。上述过程覆盖每个 batch 的每个位置,故全部输出正确。
Python ACM 题解
import math
import sys
from decimal import Decimal, ROUND_HALF_UP
def format_two_digits(x):
value = Decimal(str(x)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
if value == 0:
value = Decimal("0.00")
return format(value, ".2f")
def solve():
input = sys.stdin.buffer.readline
first = input().split()
if len(first) != 4:
return
batch_size, n, d, window = map(int, first)
if batch_size < 1 or n < 1 or d < 1 or not (1 <= window <= n):
print(0)
return
scale = math.sqrt(d)
for batch_index in range(batch_size):
q = [list(map(float, input().split())) for _ in range(n)]
k = [list(map(float, input().split())) for _ in range(n)]
v = [list(map(float, input().split())) for _ in range(n)]
if batch_index:
print()
for i in range(n):
left = max(0, i - window)
right = min(n, i + window + 1)
scores = []
for j in range(left, right):
dot = sum(q[i][t] * k[j][t] for t in range(d))
scores.append(dot / scale)
maximum = max(scores)
exponentials = [math.exp(score - maximum) for score in scores]
denominator = sum(exponentials)
result = []
for t in range(d):
numerator = sum(
exponentials[j - left] * v[j][t]
for j in range(left, right)
)
result.append(numerator / denominator)
print(" ".join(format_two_digits(x) for x in result))
if __name__ == "__main__":
solve()
复杂度分析
令 $W_i$ 为位置 $i$ 的实际窗口长度,则 $W_i\le\min(N,2\omega+1)$。
时间复杂度:$O!\left(BN\min(N,2\omega+1)d_k\right)$,其中 $B$ 为 batch_size。当把 $\omega$ 视为局部小窗口时,也可写作 $O(BN\omega d_k)$。
空间复杂度:输入矩阵和当前组输出计算占 $O(Nd_k)$,单行分数与权重占 $O(\min(N,2\omega+1))$;不构造 $N^2$ 注意力矩阵。
注意事项
window_size是半径;序列内部最多有 $2\omega+1$ 个位置。- Softmax 的最大值和分母都只能在当前窗口内计算。
- 必须先用完整精度权重完成加权,再格式化最终结果。
- 非法参数要在读取后续矩阵之前处理,样例 2 不提供可供正常计算的合法矩阵数据。
window_size=seq_len虽然合法,但与seq_len-1的覆盖效果相同。
第 2 题:全整型量化的矩阵乘法
题面
线性对称量化采用
\[q=\operatorname{round}(r/s),\qquad r\approx s q,\]其中 8 bit 对称量化的整数范围是 $[-127,127]$,一列实数的缩放因子为
\[s=\frac{\max_t\lvert r_t\rvert}{127}.\]缩放因子继续表示为
\[s\approx\frac{a}{2^b},\qquad 0\le a\le127,\quad 0\le b\le30.\]对 B 的第 $j$ 列,按材料可恢复出的选择规则,取
\[b_j=\max\left\{b\in[0,30]: \operatorname{round}(s_j2^b)\le127\right\}, \qquad a_j=\operatorname{round}(s_j2^{b_j}).\]矩阵 A 已按行量化。若 A 的第 $i$ 行表示为整数向量 $q^A_i$ 和缩放因子 $a^A_i/2^{b^A_i}$,B 的第 $j$ 列量化为 $q^B_j$ 和 $a^B_j/2^{b^B_j}$,则
\[C_{ij}\approx \left(\sum_t q^A_{it}q^B_{tj}\right) \frac{a^A_i a^B_j}{2^{b^A_i+b^B_j}}.\]每个 $C_{ij}$ 输出三个整数:
\[q^C_{ij}=\sum_tq^A_{it}q^B_{tj},\qquad a^C_{ij}=a^A_i a^B_j,\qquad b^C_{ij}=b^A_i+b^B_j.\]结果无需约简。
输入描述
第一行输入 m k n,表示 A 为 $m\times k$,B 为 $k\times n$。
接下来 $m$ 行描述已经量化的 A。每行包含 $k+2$ 个整数:前 $k$ 个是该行的量化值,最后两个是该行缩放因子的 $a,b$。
接下来 $n$ 行描述 B,每行包含 $k$ 个浮点数,表示 B 的一列。注意:B 是按列输入,而不是按行输入。
输出描述
输出 $m$ 行。每行包含 $n$ 组三元组,每组三个整数依次为该元素的 $q,a,b$,即每行共 3n 个整数。
约束
\[1\le m,k,n\le100.\]A 和量化后 B 的量化值范围为 $[-127,127]$;输入缩放因子的参数满足 $0\le a\le127$、$0\le b\le30$。输出缩放因子无需约简,因此输出的 $a,b$ 可以超过单个输入缩放因子的范围。
现有材料没有定义或保证以下边界:
- B 中浮点数的范围,以及是否可能出现 NaN 或无穷大;
- 当 B 某列全为 0 时应输出哪个 $b$;
- 当缩放因子过大、连 $b=0$ 都无法使 $a\le127$ 时应如何输出;
round在负数恰好位于半整数时采用何种规则。
为使实现确定且不暗中猜测,本文明确采用以下约定:
- 输入 B 必须是有限十进制数;代码用
Decimal按输入文本精确读取。 - 所有
round均采用 round half away from zero,即取最近整数,恰好一半时向远离 0 的方向舍入。例如 $63.5\to64$、$-63.5\to-64$。 - 对全零列,规定 $q$ 全为 0,并取 $(a,b)=(0,30)$。这是对未定义零列语义的本文约定,不是材料原题已经给出的规则。
- 对非零列,要求集合 ${b\in[0,30]:\operatorname{round}(s2^b)\le127}$ 非空。若不满足,题面没有合法输出;参考代码会显式报错,不擅自截断 $a$ 或改变缩放因子。
样例 1
输入
2 2 2
64 127 65 12
95 127 65 11
1.0 3.0
2.0 4.0
输出
18817 6305 24 20225 4225 23
20119 6305 23 22209 4225 22
样例 2
输入
2 2 1
64 127 65 12
95 127 65 11
1.0 3.0
输出
18817 6305 24
20119 6305 23
以样例 2 为例,B 的列为 $[1,3]$,故 $s=3/127$,量化值为 $[42,127]$;最大可行 $b=12$,且 $a=\operatorname{round}((3/127)\times4096)=97$。第一行整数点积为 $64\times42+127\times127=18817$,结果缩放因子为 $6305/2^{24}$。
算法
- 原样保存 A 每行的整数向量及参数 $(a^A_i,b^A_i)$。
- 对 B 的每一列求最大绝对值。非零列据此计算 $s$,用 half-away-from-zero 计算每个 $q^B$;再从 30 到 0 枚举 $b$,第一个使舍入后的 $a\le127$ 的值就是最大可行 $b$。全零列按本文约定直接返回全零量化向量和 $(0,30)$。
- 对每个 $(i,j)$ 计算 A 第 $i$ 行与量化后 B 第 $j$ 列的纯整数点积,并输出 $(q^C_{ij},a^A_i a^B_j,b^A_i+b^B_j)$。
正确性证明
先看 B 的单列量化。非零列的 $s=\max\lvert r\rvert/127>0$。算法对每个元素按本文明确的舍入规则计算 $q=\operatorname{round}(r/s)$,与量化公式一致;绝对值最大的元素对应幅值 127,其余元素的未舍入幅值不超过 127,因此量化值落在 $[-127,127]$。算法按 $b=30,29,\ldots,0$ 的顺序检查可行性,所以找到的第一个可行值正是题定最大可行 $b$,随后计算的 $a=\operatorname{round}(s2^b)$ 也与规则一致。对全零列,返回值严格遵循本文公开约定,并且 $sq=0$。
对任意输出位置 $(i,j)$,A 同一行共享缩放因子 $a^A_i/2^{b^A_i}$,B 同一列共享缩放因子 $a^B_j/2^{b^B_j}$。把二者从乘加和中提出,可得
\[C_{ij}\approx \left(\sum_t q^A_{it}q^B_{tj}\right) \frac{a^A_i a^B_j}{2^{b^A_i+b^B_j}}.\]算法输出的三个整数分别就是该式中的整数点积、分子乘积和指数之和,因此每个三元组都正确;遍历全部行列后,整个结果矩阵正确。
Python ACM 题解
import sys
from decimal import Decimal
from fractions import Fraction
QMAX = 127
MAX_B = 30
def round_half_away(value):
sign = -1 if value < 0 else 1
numerator = abs(value.numerator)
denominator = value.denominator
quotient, remainder = divmod(numerator, denominator)
if 2 * remainder >= denominator:
quotient += 1
return sign * quotient
def quantize_column(tokens):
decimals = [Decimal(token.decode()) for token in tokens]
if any(not value.is_finite() for value in decimals):
raise ValueError("B 中只允许有限十进制数")
column = [Fraction(value) for value in decimals]
maximum = max(abs(value) for value in column)
if maximum == 0:
return [0] * len(column), 0, MAX_B
scale = maximum / QMAX
quantized = [round_half_away(value / scale) for value in column]
chosen_b = None
chosen_a = None
for b in range(MAX_B, -1, -1):
a = round_half_away(scale * (1 << b))
if 0 <= a <= QMAX:
chosen_b = b
chosen_a = a
break
if chosen_b is None:
raise ValueError("该列 scale 无法在题定 a、b 范围内表示")
return quantized, chosen_a, chosen_b
def solve():
input = sys.stdin.buffer.readline
m, k, n = map(int, input().split())
matrix_a = []
for _ in range(m):
row = list(map(int, input().split()))
matrix_a.append((row[:k], row[k], row[k + 1]))
matrix_b = []
for _ in range(n):
tokens = input().split()
matrix_b.append(quantize_column(tokens))
for qa, aa, ba in matrix_a:
output = []
for qb, ab, bb in matrix_b:
qc = sum(x * y for x, y in zip(qa, qb))
output.extend((qc, aa * ab, ba + bb))
print(*output)
if __name__ == "__main__":
solve()
复杂度分析
时间复杂度:量化 B 需要 $O(nk+31n)$,全部整数点积需要 $O(mnk)$,总时间复杂度为 $O(mnk)$。
空间复杂度:保存量化后的 A 与 B 需要 $O(mk+nk)$;逐行生成输出时,除输入存储外的输出暂存为 $O(n)$。
注意事项
- B 按列输入,每一行输入的是一整列。
- Python 内置
round使用 ties-to-even,不能直接实现本文约定的 half-away-from-zero;floor(x+0.5)对负半数也会出错。 - 全零列会使直接计算
value / scale除以 0,必须先按公开约定单独处理。 - 不能在找不到合法 $b$ 时默默返回 0,也不能把超范围的 $a$ 强行截到 127;材料未定义这种输入的输出。
- 量化后的 B 整数受 $[-127,127]$ 限制,但整数点积以及输出 scale 的 $a,b$ 无需约简,可能超过单个量化参数的范围。