ARTICLE · 1000787
华为机考AI方向9月2日笔试题与解析
写在前面
本次给大家带来2026年9月2日华为AI方向笔试题的2道题,本套题涉及到的岗位:AI算法工程师,AI应用开发工程师,AI数据科学工程师等以AI为开头的岗位,统一笔试,不区分部门。
需要我整理的AI机考速成攻略&题单可访问文章底部左侧:阅读原文
选择题(15*6分 + 5*12分)
1、在LLM推理服务中,prefill与decode两阶段的典型瓶颈分别更接近哪种特征:
二者都只受CPU线程数限制 prefill主要受算力限制,decode主要受内存/访存限制 prefill主要受内存带宽限制,decode主要受算力限制 二者都只受网络带宽限制
2、在Transformer文本分类模型训练中,观察到如下表现:训练集精度持续上升至97%,训练loss持续下降;验证集精度上升到一定峰值后明显回落,验证loss持续上升。据此判断与应对方案,不合理的是()
可适当增大权重衰减(L2正则)缓解过拟合 模型出现欠拟合,需要增大模型容量 模型出现过拟合,泛化能力下降 可采用早停(Early Stopping)在验证性能最优处停止
3、在使用PCA进行降维时,通常通过什么指标来选择保留的主成分数量
数据的均值 特征值的累计贡献率 特征向量的模长 投影后的样本方差倒数
4、在Transformer Block中,Residual Add(残差相加)后通常紧接着执行RMSNorm。在推理优化中,这两个算子融合的主要目的是?
避免残差结果写回HBM,直接在片上完成归一化 残差连接无法计算 为了增加FLOPs 硬件不支持加法
5、在Top-P(Nucleus Sampling,核采样)策略中,参数p=0.9代表什么含义?
抛弃概率低于0.9的Token 从概率最高的前90个Token中进行采样 将词表按概率降序排列,截取累加概率刚好超过0.9的最小Token集合,从中重新归一化并采样 随机以90%的概率选择概率最高的Token
6、FlashAttention Kernel的核心思想是将Attention的多次matmul及softmax等计算融合成一个kernel,其主要目的是?
减少HBM访问次数 减少模型参数 减少batch size 减少FLOPs
7、所谓“非对称量化”比“对称量化”多了一个参数,它是:
偏移量 Z 权重矩阵 W 缩放因子 S 激活函数 σ
8、在机器学习模型评估中,如果任务对‘漏检正样本’的代价特别敏感,应优先关注以下哪个指标?{{ select(8) }}
Top-1准确率 准确率(Accuracy) 精确率(Precision) 召回率(Recall)
9、通用近似定理主要说明了什么?
增加网络深度一定比增加网络宽度效果更好 只要激活函数是非线性的(如Sigmoid),包含足够多隐藏层神经元的单层隐藏层网络就可以逼近任何连续函数 神经网络总能找到全局最优解 只要激活函数是线性的,包含至少一个隐藏层的神经网络就可以以任意精度逼近任何连续函数
10、对于单样本损失 ,关于参数w的梯度(损失对参数的导数)为?
限于篇幅,其余选择题及其解析请到网站上查看
第2题-滑动窗口注意力机制(150分)
标准 Transformer 的自注意力计算为:
对于序列长度为 、特征维度为 的输入,标准自注意力的时间复杂度为 ,空间复杂度为 。
研究表明,许多任务中的依赖关系具有较强的局部性,远距离 token 往往可以通过多层网络间接完成信息交互。因此,可以限制每个 Query 只与附近一定范围内的 Key 计算注意力,从而降低计算开销。
滑动窗口注意力的规则如下:
为每个 token 设置大小为 的对称窗口。 对于位置 ,仅与满足 的位置 计算注意力。 当窗口超出序列边界时,只保留合法位置。 为保证 Softmax 的数值稳定性,在计算指数前减去当前窗口中的最大值:
例如,当 时,各位置对应的窗口为:
位置 : 位置 : 位置 : 位置 : 位置 : 位置 : 位置 : 位置 :
对于每个位置 ,需要计算:
其中 仅取当前位置的滑动窗口范围。对这些分数进行 Softmax 后,再与对应的 Value 加权求和,得到当前位置的输出向量。
输入描述
第一行包含四个整数:
batch_size seq_len d_k window_size
随后输入 组数据,每组依次包含:
行 Query 矩阵,每行包含 个浮点数; 行 Key 矩阵,每行包含 个浮点数; 行 Value 矩阵,每行包含 个浮点数。
约束条件:
输出描述
输出 组结果。
每组包含 行,每行 个浮点数,表示对应位置经过滑动窗口注意力计算后的输出向量。
不同组之间用一个空行分隔,最后一组后不输出空行。
所有结果保留两位小数,四舍五入。
如果输入参数违反约束条件,输出:
0样例 1
输入:
1 4 4 10.1 0.2 0.3 0.40.2 0.3 0.4 0.50.3 0.4 0.5 0.60.4 0.5 0.6 0.70.1 0.0 0.1 0.00.0 0.1 0.0 0.10.1 0.1 0.0 0.00.0 0.0 0.1 0.11.0 0.0 0.0 0.00.0 1.0 0.0 0.00.0 0.0 1.0 0.00.0 0.0 0.0 1.0输出:
0.50 0.50 0.00 0.000.33 0.34 0.33 0.000.00 0.33 0.33 0.340.00 0.00 0.50 0.50说明:
输入中:
缩放因子为:
位置 只与位置 计算注意力;
位置 与位置 计算注意力;
位置 与位置 计算注意力;
位置 只与位置 计算注意力。
对窗口内的注意力分数进行 Softmax,并对对应的 Value 加权求和,即可得到上述结果。
提示
仅可使用编程语言内置函数。
解题思路
滑动窗口注意力与标准自注意力的区别在于:对于位置 的 Query,只计算其附近窗口中的 Key,而不再与全部 个 Key 计算注意力。
设序列长度为 ,窗口大小为 ,对于位置 ,实际参与计算的位置范围为:
因此位置 只需要与 中的 Key 计算注意力分数。
对于每个位置 ,按照以下步骤计算:
计算缩放因子:
对窗口中的每个位置 计算注意力分数:
其中点积为:
为保证 Softmax 的数值稳定性,先求当前窗口内的最大分数:
再计算:
以及:
最终注意力权重为:
使用这些权重对窗口中的 Value 加权求和:
由于窗口外的位置完全不参与计算,因此不需要构造完整的 注意力矩阵。
实现时,对每个 batch 独立处理。对于每个 Query 位置,只保存当前窗口中的注意力分数和 Softmax 中间结果,然后直接计算最终输出。
在开始计算前需要检查:
如果任意约束不满足,直接输出 0。
代码实现
python代码(C++和JAVA代码见在线OJ网址)
import sysimport mathdefattention(q, k, v, n, d, w):# 缩放因子 sqrt(d_k) scale = math.sqrt(d)# 保存当前 batch 的最终输出 ans = [[0.0] * d for _ in range(n)]for i in range(n):# 当前 Query 能访问的左右边界 left = max(0, i - w) right = min(n - 1, i + w) scores = []# 计算当前窗口内 Q[i] 与各个 K[j] 的注意力分数for j in range(left, right + 1): dot = 0.0for t in range(d): dot += q[i][t] * k[j][t] scores.append(dot / scale)# Softmax 前减去最大值,避免指数运算发生数值溢出 mx = max(scores) exps = [] total = 0.0for score in scores: e = math.exp(score - mx) exps.append(e) total += e# 根据 Softmax 权重对窗口内的 Value 加权求和for idx in range(len(scores)): j = left + idx weight = exps[idx] / totalfor t in range(d): ans[i][t] += weight * v[j][t]return ansdefmain(): data = sys.stdin.buffer.read().split()if len(data) < 4: print(0)return b = int(data[0]) n = int(data[1]) d = int(data[2]) w = int(data[3])# 检查题目规定的参数范围if b < 1or n < 1or d < 1or w < 1or w > n: print(0)return pos = 4 all_ans = []for _ in range(b): q = [[0.0] * d for _ in range(n)] k = [[0.0] * d for _ in range(n)] v = [[0.0] * d for _ in range(n)]# 读取 Query 矩阵for i in range(n):for j in range(d): q[i][j] = float(data[pos]) pos += 1# 读取 Key 矩阵for i in range(n):for j in range(d): k[i][j] = float(data[pos]) pos += 1# 读取 Value 矩阵for i in range(n):for j in range(d): v[i][j] = float(data[pos]) pos += 1 all_ans.append(attention(q, k, v, n, d, w))# 按照题目要求输出,每两个 batch 之间空一行for x in range(b):for row in all_ans[x]: print(" ".join(f"{val:.2f}"for val in row))if x != b - 1: print()if __name__ == "__main__": main()第3题-全整型量化的矩阵乘法(300分)
在模型量化中,通常需要将高精度数据类型(如 float)转换为低精度整数类型(如 int8)。一种常见的线性量化方式如下。
量化公式:
反量化公式:
其中, 表示原始浮点数, 表示量化后的整数, 表示缩放因子。对于 bit 量化,缩放因子可以计算为:
利用上述方法,可以按行或按列将浮点矩阵量化为整数矩阵,同时为每一行或每一列额外保存一个缩放因子。
为了进一步避免在计算过程中使用浮点数,还可以将缩放因子 表示为两个整数 和 :
这样,缩放因子之间的乘法和加法就可以转化为整数运算、移位和加法操作。
例如:
对于乘法:
对于加法,可以先将两个数对齐到相同的指数:
现在需要根据以上规则,完成矩阵的全整型量化与乘法计算。
给定已经完成全整型量化的矩阵 ,以及尚未量化的浮点矩阵 ,请计算矩阵乘积 ,并以全整型量化的形式输出结果矩阵 。
输入
第一行输入三个整数:
m k n
表示矩阵 的大小为 ,矩阵 的大小为 。
接下来输入 行,每行包含 个整数:
前 个整数表示矩阵 当前行量化后的整数值,取值范围为 [-127,127];最后两个整数分别为 和 ,表示当前行对应的缩放因子:
其中:
的取值范围为 [0,127];的取值范围为 [0,30]。
接下来输入矩阵 。
为了便于按列进行量化,输入按照 的形式给出,即接下来输入 行,每行包含 个浮点数,其中第 行表示矩阵 的第 列。
输出
输出全整型量化后的结果矩阵 。
矩阵 的大小为 ,每个元素使用三个整数表示,依次为:
量化值 scale的a scale的b
其中对应的缩放因子为:
矩阵 在量化时采用 8 bit 量化,即量化后的整数范围为 [-127,127]。
需要注意,结果矩阵中的“量化值”是两个整数量化向量的点积,因此不需要再次限制在 [-127,127] 范围内。
结果的缩放因子也无需进行约分。
样例 1
输入
2 2 264 127 65 1295 127 65 111.0 3.02.0 4.0输出
18817 6305 24 20225 4225 2320119 6305 23 22209 4225 22说明
输出共有 行,每行包含 个结果元素。
每个结果元素均使用三个整数表示,分别对应量化值以及缩放因子的 、。
解题思路
矩阵 已经完成了按行量化,每一行都给出了:
整数量化值 缩放因子
因此只需要对矩阵 做同样的 8 bit 量化,然后直接计算整数点积。
题目中 的数据实际上按照 输入。也就是说,每一行对应 的一列。样例 2 中 是 ,输入为一行 1.0 3.0,也验证了这一点。
对于 的某一列 ,首先计算真实缩放因子:
然后量化:
并限制到 。
接下来还需要把浮点缩放因子 表示成:
其中 ,。
为了尽量提高缩放因子的表示精度,可以从 开始向下寻找,第一个满足
的位置,就是所需的 。
例如:
当 时:
所以得到:
与样例一致。
对于结果 , 的第 行和 的第 列分别表示为:
因此:
所以输出的三个整数直接为:
题目明确说明结果无需再次压缩到 int8,也无需对缩放因子进行约分。
实现时,Python 使用 numpy:
对输入的 按行计算最大绝对值; 得到每一列的量化整数和整数缩放因子; 用 A @ Bq.T一次计算所有整数点积;组合对应的 和 输出结果。
如果某列全部为 ,直接令其量化结果全为 ,缩放因子记为 。
代码实现
python代码(C++和JAVA代码见在线OJ网址)
import sysimport numpy as np# 按题目规则进行四舍五入,避免不同语言的 round 行为不同defrnd(x):return np.sign(x) * np.floor(np.abs(x) + 0.5)# 把浮点缩放因子表示为 a / 2^bdefget_ab(s):if s == 0:return0, 0for b in range(30, -1, -1): a = int(s * (1 << b) + 0.5)if a <= 127:return a, breturn0, 0# 对 B 的转置按行量化,每一行就是 B 的一列defquant_b(bt): n, k = bt.shape q = np.zeros((n, k), dtype=np.int64) aa = np.zeros(n, dtype=np.int64) bb = np.zeros(n, dtype=np.int64)for i in range(n): mx = np.max(np.abs(bt[i]))if mx == 0:continue s = mx / 127.0# 先按照真实 scale 完成 int8 量化 x = rnd(bt[i] / s) x = np.clip(x, -127, 127) q[i] = x.astype(np.int64)# 再把 scale 本身量化为 a / 2^b aa[i], bb[i] = get_ab(s)return q, aa, bb# 完成全整型矩阵乘法defsolve(aq, aa, ab, bt): bq, ba, bb = quant_b(bt)# aq: m*k,bq.T: k*n val = aq @ bq.T sa = aa[:, None] * ba[None, :] sb = ab[:, None] + bb[None, :]return val, sa, sbdefmain(): input = sys.stdin.readline m, k, n = map(int, input().split()) aq = np.zeros((m, k), dtype=np.int64) aa = np.zeros(m, dtype=np.int64) ab = np.zeros(m, dtype=np.int64)for i in range(m): x = list(map(int, input().split())) aq[i] = x[:k] aa[i] = x[k] ab[i] = x[k + 1]# 题目实际按 B^T 输入,因此共有 n 行,每行 k 个数 bt = np.zeros((n, k), dtype=np.float64)for i in range(n): bt[i] = list(map(float, input().split())) val, sa, sb = solve(aq, aa, ab, bt)for i in range(m): out = []for j in range(n): out.extend([ str(val[i, j]), str(sa[i, j]), str(sb[i, j]) ]) print(" ".join(out))if __name__ == "__main__": main()刷题练习:CodeFun2000.com