大厂真题 / 华为
华为 2026-10-10 笔试真题 - AI岗
本场考试概述
考试时间:2026年10月10日
考试岗位:AI岗
难度评级:中等
考点分析:机器学习基础、K-Median 聚类与分组查询注意力。
收录范围:本页包含已披露的第 1—10 道选择题与 2 道编程题。选择题概述为 20 道,但第 11—20 道未披露,不补写缺失题目,也不补充未给出的样例或约束。
建议策略:先核对矩阵形状、掩码方向和浮点输出,再实现计算流程。聚类题按指定迭代规则运行,不把局部迭代结果误当成全局最优保证。第一题使用 NumPy,运行环境需要具备该依赖。
选择题:已披露的 10 道
1. 半监督学习与强化学习
以下哪项描述正确?
- A. 半监督学习只能使用有标签数据
- B. 半监督学习需要与环境交互来获取奖励
- C. 强化学习利用奖励信号指导学习过程
- D. 强化学习无需任何数据,只靠完全随机探索
答案:C。 半监督学习同时利用有标签和无标签样本;强化学习从环境交互中的奖励获取学习信号,交互轨迹本身也是数据。
2. 7B 模型的 INT4 权重存储
只计算权重,一个 7B 参数模型采用 INT4 量化后大约占用多少空间?
- A. 3.5 GB
- B. 28 GB
- C. 14 GB
- D. 7 GB
答案:A。 每个权重占 4 bit,按十进制容量估算为 $7\times10^9\times4/8=3.5\times10^9$ 字节。量化元数据、KV Cache 和运行时缓冲区不在这项估算内。
3. 浮点机器精度
以下哪项最接近机器精度 eps 的含义?
- A. 使浮点计算中的 $1+eps>1$ 成立的最小正增量
- B. 最大可表示正数
- C. 使 $eps^2$ 约等于零的最小正数
- D. 最小可表示正数
答案:A(题目给出的最接近选项)。 常用库中的机器 epsilon 指 1 与下一个更大可表示浮点数之间的差值;严格说,任意增量经舍入后能改变 1 的阈值还与舍入规则有关。它衡量 1 附近的分辨率,不是浮点数的数值范围。
4. Prefill 阶段
生成式模型推理时,Prefill 阶段的主要工作是什么?
- A. 压缩模型
- B. 一次性处理输入 prompt 并计算、缓存 KV
- C. 逐 token 生成后续输出
- D. 评估模型准确率
答案:B。 Prefill 处理输入序列并建立 KV Cache;后续 Decode 则利用缓存逐步生成。首个输出 token 可由 Prefill 的结果采样得到。
5. 均值与极差
两种路径算法各有五组 (长度/m, 耗时/s) 数据:
- X:
(12.5,8.2), (13.2,9.0), (11.8,7.5), (12.0,8.0), (12.5,8.3) - Y:
(14.0,10.5), (13.5,9.8), (15.2,11.2), (14.8,10.8), (14.5,10.0)
X 的长度均值与 Y 的耗时极差分别是多少?
- A. 12.4 m,1.7 s
- B. 12.4 m,1.4 s
- C. 12.5 m,1.4 s
- D. 12.5 m,1.7 s
答案:B。 长度之和为 62.0,均值为 12.4;Y 耗时最大值为 11.2、最小值为 9.8,极差为 1.4。
6. 矩阵乘积维度
$A\in\mathbb R^{m\times n}$、$B\in\mathbb R^{n\times p}$,则 $AB$ 的形状为:
- A. $p\times m$
- B. $n\times n$
- C. $m\times p$
- D. $m\times n$
答案:C。 内侧维度相同才能相乘,结果保留左矩阵的行数与右矩阵的列数。
7. 随机变量的本质
- A. 描述频率的统计量
- B. 一个未知数值的变量
- C. 计算期望的工具
- D. 定义在概率空间上的可测函数
答案:D。 随机变量是样本空间到数值空间的可测映射,可测性保证与取值相关的事件能够被赋予概率。
8. PCA 的主要作用
- A. 特征转换
- B. 特征编码
- C. 特征降维
- D. 特征构造
答案:C。 PCA 通过投影到保留方差较大的主成分方向来降维。该过程确实包含线性变换,但本题问的是主要用途。
9. 奇异值分解
实矩阵完整奇异值分解 $A=U\Sigma V^T$ 中,关于 $U,V$ 哪项正确?
- A. 都是对角矩阵
- B. 元素必须全部为正
- C. 都是正交矩阵
- D. 分别是上三角和下三角矩阵
答案:C。 完整 SVD 的左右奇异向量分别组成正交矩阵。若采用截断或紧致表示,则对应的是列正交,未必为方阵。
10. 端侧 INT4 量化
采用 INT4 权重量化的主要直接收益是什么?
- A. 降低权重存储与运行内存占用
- B. 完全避免冷启动
- C. 消除所有 KV Cache
- D. 保证提高模型精度
答案:A。 更低的权重位宽减少权重存储与加载压力;冷启动还涉及初始化,KV Cache 仍需存储,量化也不保证精度提高。
第 1 题:K-Median 聚类
题目描述
给定 $n$ 个 $d$ 维样本,按以下固定流程划分为 $k$ 个簇:
- 取前 $k$ 个样本作为初始中心,编号从零开始。
- 按曼哈顿距离将样本分给最近的中心,距离相等时选择编号最小的簇。
- 对每个非空簇,逐维取中位数作为新中心。偶数个数时取中间两数的平均值;空簇保持原中心。
- 当全部中心所有坐标的绝对变化总和小于 $10^{-4}$,或完成 100 轮分配与更新时停止。
输出最后一轮分配的标签、更新后的中心,以及按这些标签和最终中心计算的曼哈顿距离总和 inertia。停止后不额外再做一轮分配。
输入格式:第一行 $n,d,k$,其中 $1\le n\le5000$、$1\le d\le20$、$1\le k\le\min(100,n)$。随后 $n$ 行,每行 $d$ 个实数,范围为 $[-10^4,10^4]$。
输出格式:首行输出 $n$ 个标签;接下来 $k$ 行输出各中心坐标,最后一行输出 inertia。所有实数保留六位小数。
这里要求实现给定的交替迭代过程,并不保证求得聚类目标的全局最优解。初始样本可能重复,因此从第一轮起就需要处理空簇。
样例 1
输入
5 2 1
1 2
0 10
10 0
10 10
5 5
输出
0 0 0 0 0
5.000000 5.000000
37.000000
样例 2
输入
4 2 2
0 0
1 1
2 2
100 100
输出
0 0 0 1
1.000000 1.000000
100.000000 100.000000
4.000000
思路分析
第一步:分配为何使用曼哈顿距离。 对每个样本计算到各中心的逐维绝对差之和。按中心编号遍历并仅在严格更小时更新,或使用返回第一个最小值的 argmin,均可实现相等距离取最小编号。
第二步:更新为何使用中位数。 固定簇成员后,各坐标互相独立。一维目标 $\sum_i\lvert x_i-c\rvert$ 在中位数处取得最小值:向右移动时,左侧点的距离增加、右侧点的距离减少,两边数量平衡时最优。偶数个样本的中间区间都能达到最小值,本题指定采用中间两数的平均值,不能任取一端。
第三步:控制距离矩阵的内存。 不一次生成 $n\times k\times d$ 张量,而是逐维广播得到 $n\times k$ 的差值矩阵,再累加到距离矩阵。使用 NumPy 向量化完成主要数值计算,避免在最大规模下用 Python 三重循环逐元素运行。
第四步:严格遵守轮次边界。 每轮先分配、再更新,再检查所有中心的总变化。空簇直接保留旧中心。输出使用该轮的标签和更新结果,在达到轮数上限时尤其不能擅自多做一次分配。样例一只有一个簇,各坐标中位数均为 5,距离总和为 37。
正确性说明:分配函数逐项计算规定距离并正确打破平局;更新函数独立计算每一簇每一维的指定中位数,并保留空簇。循环严格按顺序执行最多 100 轮,故输出与题设过程一致。
题解代码
import sys
import numpy as np
def k_median(points, k):
n, d = points.shape
centers = points[:k].copy()
for _ in range(100):
distances = np.zeros((n, k), dtype=float)
for axis in range(d):
distances += np.abs(points[:, axis, None] - centers[None, :, axis])
labels = distances.argmin(axis=1)
updated = centers.copy()
for cluster in range(k):
members = points[labels == cluster]
if len(members):
updated[cluster] = np.median(members, axis=0)
shift = np.abs(updated - centers).sum()
centers = updated
if shift < 1e-4:
break
inertia = np.abs(points - centers[labels]).sum()
return labels, centers, inertia
def solve():
input = sys.stdin.readline
n, d, k = map(int, input().split())
points = np.array([list(map(float, input().split())) for _ in range(n)])
labels, centers, inertia = k_median(points, k)
print(" ".join(map(str, labels)))
for center in centers:
print(" ".join(f"{value:.6f}" for value in center))
print(f"{inertia:.6f}")
if __name__ == "__main__":
solve()
复杂度分析
时间复杂度:$O(Tnkd)$,$T\le100$ 为实际轮数;分配需要 $O(nkd)$,簇筛选为 $O(nk)$,NumPy 中位数采用选择算法,所有簇合计处理 $O(nd)$ 个坐标值。
空间复杂度:$O(nk+nd+kd)$,包括距离矩阵、样本、簇成员与中心。
第 2 题:分组查询注意力
题目描述
实现 GQA:给定序列长度 $n$、Query 头数 $h_q$、KV 头数 $h_{kv}$、单个 Q/K 头维度 $d_k$ 和单个 V 头维度 $d_v$。保证 $h_q$ 是 $h_{kv}$ 的整数倍,令 $g=h_q/h_{kv}$,Query 头 $h$ 共享 KV 头 $\lfloor h/g\rfloor$。
每个头取矩阵中的连续列块。对单头计算缩放分数 $S=QK^T/\sqrt{d_k}$,掩码为 1 的位置将分数覆盖为 $-10^{30}$,掩码为 0 的位置保留。逐行减去最大分数,再计算 softmax 权重,与对应的 V 头加权求和。最后按 Query 头编号拼接输出。
输入格式:第一行 $n,h_q,h_{kv},d_k,d_v$,满足 $1\le n\le100$、$1\le h_q\le16$、$1\le h_{kv}\le8$、$1\le d_k,d_v\le10$。然后依次输入:
- $n$ 行 Q,每行 $h_qd_k$ 个浮点数。
- $n$ 行 K,每行 $h_{kv}d_k$ 个浮点数。
- $n$ 行 V,每行 $h_{kv}d_v$ 个浮点数。
- $n$ 行掩码,每行 $n$ 个 0 或 1。
Q、K、V 的数值范围为 $[-10,10]$。
输出格式:输出 $n$ 行,每行 $h_qd_v$ 个数,保留两位小数。
掩码边界:这里采用题目允许的有限替代值 $-10^{30}$。若某行全部被屏蔽,所有替代分数相等,softmax 会得到均匀权重;这与使用真正的负无穷时可能出现未定义值不同,不应自行把全屏蔽行改为零向量。
样例 1
输入
2 4 2 2 2
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0
9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0
1.0 2.0 3.0 4.0
5.0 6.0 7.0 8.0
1.0 2.0 3.0 4.0
5.0 6.0 7.0 8.0
0 0
0 0
输出
5.00 6.00 5.00 6.00 7.00 8.00 7.00 8.00
5.00 6.00 5.00 6.00 7.00 8.00 7.00 8.00
样例 2
输入
2 2 1 1 1
1.0 2.0
3.0 4.0
5.0
6.0
7.0
8.0
0 1
0 0
输出
7.00 7.00
7.95 7.98
样例 3
输入
2 1 1 2 1
1.0 2.0
3.0 4.0
1.0 0.0
0.0 1.0
5.0
10.0
0 0
0 0
输出
8.35
8.35
思路分析
第一步:写清楚三套列偏移。 Query 头 $h$ 的起点是 $hd_k$,对应 KV 头编号为 $q=\lfloor h/g\rfloor$,K 和 V 的起点分别是 $qd_k$、$qd_v$。不能用取模代替整除,否则会把连续分组变成交错分组。
第二步:逐行计算稳定 softmax。 对 Query 行 $i$,与每个 Key 行 $j$ 做点积,再除以 $\sqrt{d_k}$。覆盖掩码位置后,取当前行最大值 $s_{max}$,计算 $w_j=\exp(S_{ij}-s_{max})$。减去同一个常数不改变归一化结果,同时避免指数上溢。
第三步:直接累加 V。 每个输出坐标等于 $\sum_j w_j V_{jt}/\sum_j w_j$。只需保留当前行分数和权重,不必存完整注意力矩阵;把结果写入当前 Query 头对应的输出列块。
样例二的第一行只能看到第一个 Key,因此两个 Query 头都输出 7.00;第二行可见两项,两个 Query 头因分数不同,分别得到 7.95 与 7.98。
正确性说明:列偏移保证每个 Query 头读取指定共享组;点积、缩放、掩码、稳定归一化、V 加权和依次对应定义。按头编号写入连续列块,正好实现最终拼接。
题解代码
import sys
import math
def grouped_attention(query, key, value, mask, hq, hkv, dk, dv):
n = len(query)
group = hq // hkv
output = [[0.0] * (hq * dv) for _ in range(n)]
scale = math.sqrt(dk)
for head in range(hq):
shared = head // group
q_start, k_start, v_start = head * dk, shared * dk, shared * dv
for i in range(n):
scores = []
for j in range(n):
score = sum(query[i][q_start + t] * key[j][k_start + t]
for t in range(dk)) / scale
scores.append(-1e30 if mask[i][j] else score)
maximum = max(scores)
weights = [math.exp(score - maximum) for score in scores]
total = sum(weights)
for t in range(dv):
output[i][head * dv + t] = sum(
weights[j] * value[j][v_start + t] for j in range(n)
) / total
return output
def solve():
input = sys.stdin.readline
n, hq, hkv, dk, dv = map(int, input().split())
query = [list(map(float, input().split())) for _ in range(n)]
key = [list(map(float, input().split())) for _ in range(n)]
value = [list(map(float, input().split())) for _ in range(n)]
mask = [list(map(int, input().split())) for _ in range(n)]
output = grouped_attention(query, key, value, mask, hq, hkv, dk, dv)
for row in output:
print(" ".join(f"{x:.2f}" for x in row))
if __name__ == "__main__":
solve()
复杂度分析
时间复杂度:$O(h_qn^2(d_k+d_v))$,每个头计算所有 Query/Key 点积及 V 加权和。
空间复杂度:$O(n^2+nh_q(d_k+d_v)+nh_{kv}(d_k+d_v))$,包括掩码、输入及输出;单行临时缓冲仅为 $O(n)$。
小结
- K-Median 的核心是曼哈顿距离与逐维中位数的配对,平局、空簇和最后一轮输出必须严格实现。
- GQA 的共享关系由连续分组决定,矩阵列偏移和掩码数值约定比直接套用多头注意力模板更重要。
- 选择题应区分权重容量与完整推理显存、维度与数值范围,以及算法定义与实现约定。