大厂真题 / 华为
华为 2026-10-23 笔试真题 - AI岗
本场考试概述
考试时间:2026年10月23日
考试岗位:AI岗
难度评级:中等偏易
收录范围:材料标注选择题共 20 道,但完整披露的题干、选项与答案只有前 6 道单选题;本页仅收录这 6 道及 2 道编程题,不根据概述补造其余选择题。材料概述另提到因果掩码、GQA/MQA、推理加速、K-Means、KNN、F1、数据布局、矩阵分解和修正牛顿法,未披露的具体题目不展开。
考点分析:
- 已披露选择题:自注意力计算复杂度、池化、PCA、模型评测、线性相关与泊松分布。
- 第一题:KV Cache 内存池管理,核心是向上取整与排序贪心。
- 第二题:OCR 智能纠错,核心是平滑概率、整数分数比较与同步自训练模拟。
建议策略:先把业务背景转成明确的计数与约束。第一题求最终最多保留多少请求,不必模拟缓存释放;第二题严格区分“用旧模型统一预测”和“重建新模型”,并落实平局时取字典序最小的规则。
选择题:已披露的 6 道
1. 自注意力分数矩阵的复杂度
假设输入序列长度为 $n$,每个 token 的维度为 $d$,在标准自注意力中,计算注意力分数矩阵 $QK^T$ 的复杂度是多少?
- A. $O(nd^2)$
- B. $O(nd)$
- C. $O(n^2d)$
- D. $O(n^2d^2)$
答案:C。 $Q,K$ 均为 $n\times d$ 矩阵,结果包含 $n^2$ 个元素,每个元素需要一次长度为 $d$ 的内积,故为 $O(n^2d)$。不要与生成 Q、K 的线性投影复杂度混淆。
2. 池化层的主要作用
池化层(POOLING)主要作用是:
- A. 减少计算量
- B. 增加非线性
- C. 提取特征
- D. 增加感受野
答案:A。 按本题单选口径,池化的主要作用是下采样,减小特征图的空间尺寸,从而降低后续计算量。最大池化也具有非线性,池化也可能扩大有效感受野,但不是此题要求的主要作用。注意:特征图变小不意味着后续卷积核的参数量必然减少;卷积参数量还取决于核大小与通道数。
3. PCA 的性质
关于 PCA,下列说法正确的是:
- A. PCA 是有监督降维,会利用标签 $y$
- B. PCA 寻找使类间距离最大的投影方向
- C. PCA 的主成分是原特征的线性组合,按方差从大到小排序
- D. PCA 对各特征的量纲不敏感,因此无需标准化
答案:C。 PCA 是无监督线性降维方法,选取投影方差较大的正交方向。它不使用分类标签,也不是按类间距离确定方向。特征量纲会影响方差及主成分,是否标准化应结合特征含义决定,不能认定总是不需要。
4. 固定标准答案的评测指标
在评测大模型的数学推理能力时,通常会使用含有 8500 道小学数学应用题的数据集 GSM8K。对于这类有固定标准答案的题目,最常用的评测指标是:
- A. 准确率(Accuracy)
- B. 语义相似度(Cosine Similarity)
- C. 困惑度(Perplexity)
- D. 训练时长(Training Time)
答案:A。 按规定抽取、规范化最终答案后,与标准答案比较,统计答对比例。语义相似不能代替数值答案判定,困惑度与训练时长也不直接衡量这类题目的正确率。
5. 线性相关的定义
线性相关的向量组满足?
- A. 向量个数小于维度
- B. 任意向量不能由其他向量线性表示
- C. 存在不全为零的系数使线性组合为零
- D. 向量长度相等
答案:C。 存在不全为零的系数 $k_1,\ldots,k_s$,使
\[k_1\alpha_1+\cdots+k_s\alpha_s=0,\]就是线性相关的定义。向量个数小于维度并不能单独判断相关性;向量长度相等也不是判定条件。
6. 泊松分布
在概率论中,若随机变量 $X$ 服从泊松分布,且参数 $\lambda=2$,则 $P(X=0)$ 的值为?
- A. $e^{-2}$
- B. $1-e^{-2}$
- C. $e^{-1}$
- D. $2e^{-2}$
答案:A。 泊松分布的概率质量函数为
\[P(X=k)=\frac{\lambda^k e^{-\lambda}}{k!}.\]代入 $k=0,\lambda=2$,得到 $P(X=0)=e^{-2}$。
第 1 题:大模型推理 KV Cache 内存池管理
题目描述
在大语言模型推理过程中,KV Cache 存储历史 token 的 Key 和 Value,避免重复计算,加速自回归生成。随着序列增长,缓存占用增加,系统使用预先分配的等大小内存块(Block)管理 GPU 内存。
内存池共有 $m$ 个 Block,每个 Block 可以容纳 $k$ 个 token 的 KV 数据。现有 $n$ 个推理请求,第 $i$ 个请求处理 $len[i]$ 个 token(输入长度加生成长度),需要占用
\[b_i=\left\lceil\frac{len[i]}{k}\right\rceil\]个 Block。
分配规则如下:
- 请求按给定顺序依次到达。
- 对每个请求可以选择接受或拒绝;接受后的最终总占用不能超过 $m$,拒绝不占资源。
- 允许在后续决策中撤销之前已经接受的请求,释放其 Block,以获得更好的整体结果。
- 请求一旦被拒绝,不可再次考虑。
- 请求之间不能重排,必须按输入顺序进行决策。
求总占用不超过 $m$ 时,最终可以接受的最多请求数量。
输入描述与约束
第一行三个整数 $m,k,n$,分别表示 Block 总数、每个 Block 可容纳的 token 数和请求数。
第二行 $n$ 个整数 $len[1],\ldots,len[n]$,表示各请求的 token 数。
- $1\le m\le 100000$。
- $1\le k\le 1000$。
- $1\le n\le 100000$。
- $1\le len[i]\le 1000000$。
若某请求需要的 Block 数大于 $m$,它一定无法被接受。题目建议算法时间复杂度优于 $O(n^2)$。
输出描述
输出一个整数,表示最多可接受的请求数量。
样例 1
输入
3 10 3
25 5 15
输出
2
解释:Block 需求依次为 $3,1,2$。只保留请求 1 会占满内存,只能保留一个请求;跳过请求 1,接受请求 2、3,总占用为 $1+2=3$,可保留两个请求。
样例 2
输入
5 10 4
8 25 15 12
输出
3
解释:Block 需求依次为 $1,3,2,2$。选择请求 1、3、4,总占用 $1+2+2=5$,共接受三个请求。
解题思路:排序贪心
先区分计算顺序与请求到达顺序。 输入已经给出了全部请求,算法可以离线计算应该保留的集合;对需求排序只是求最优数量,并不是让系统重新排列到达顺序。
任取一个总需求不超过 $m$ 的集合 $S$,按照原输入顺序处理:属于 $S$ 的请求接受,其余拒绝。由于需求均为正数,任意前缀的占用都不超过整个集合的占用,因此过程始终可行。这甚至不需要执行撤销操作,也不会重新考虑被拒绝的请求。
问题因此等价于:从所有 Block 需求中选尽量多个数,使其和不超过 $m$。每个请求带来的收益都是“数量加一”,自然优先选择需求小的请求。
- 用整数式
(length + k - 1) // k计算向上取整,不使用浮点数。 - 将全部需求升序排序。
- 从小到大累加;若当前需求放不下,则停止,因为后面的需求不会更小。
正确性证明
设排序后的需求为 $b_1\le b_2\le\cdots\le b_n$。
引理一: 任意满足容量限制的子集,都能按原到达顺序合法实现。证明如上:逐个接受集合内的请求,前缀需求和不会超过集合总需求和。
引理二: 任意包含 $t$ 个请求的集合,其需求和都不小于 $\sum_{i=1}^{t}b_i$。因为最小的 $t$ 个数在所有大小为 $t$ 的选择中取得最小总和。
若贪心接受了 $r$ 个请求后停止,前 $r$ 个需求和不超过 $m$,由引理一可实现。当 $r<n$ 时,前 $r+1$ 个需求和超过 $m$,由引理二,任何 $r+1$ 个请求都不可能同时保留。因此不存在更大的可行解。若 $r=n$,已经接受全部请求,同样最优。
Python ACM 代码
import sys
def max_accept(m, k, lengths):
needs = sorted((length + k - 1) // k for length in lengths)
used = 0
answer = 0
for need in needs:
if used + need > m:
break
used += need
answer += 1
return answer
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
if not data:
return
m, k, n = data[:3]
print(max_accept(m, k, data[3:3 + n]))
if __name__ == "__main__":
main()
复杂度分析
时间复杂度:$O(n\log n)$,排序占主导,计算需求和扫描均为 $O(n)$。
空间复杂度:$O(n)$,包括输入、需求数组以及 Python 排序使用的辅助空间。
本题小结
关键是证明到达顺序不影响最终可行集合,再使用“固定数量时取最小需求”的贪心。不必套容量背包,也不必模拟复杂的缓存管理。所有请求都放不下时输出 0;恰好装满时仍应计入最后一个请求。
第 2 题:OCR 智能纠错系统迭代训练和推理
题目描述
某智能终端搭载字符级 OCR 纠错系统,把“识别字符到真实字符”的纠错建模为朴素贝叶斯过程。
字符集为小写英文字母 ${a,b,\ldots,z}$,记 $K=26$。模型由两组统计量描述:
- $emit[c][o]$:训练语料中,真实字符 $c$ 被 OCR 识别为 $o$ 的次数。
- $prior[c]$:训练语料中,真实字符 $c$ 出现的次数。
对发射概率和先验概率使用加 $\alpha$ 拉普拉斯平滑,$\alpha$ 是正整数:
\[P(o\mid c)=\frac{emit[c][o]+\alpha} {\sum_{o'}emit[c][o']+\alpha K}, \qquad P(c)=\frac{prior[c]+\alpha} {\sum_{c'}prior[c']+\alpha K}.\]对于待纠错字符串中的每一个 OCR 字符 $o$,独立选择使 $P(o\mid c)P(c)$ 最大的真实字符 $c$。如果多个候选取得相同最大值,选择字典序最小的字符。
除 $N$ 条带标注训练对外,还有 $M$ 条只有 OCR 输出、没有真实文本的未标注样本。初始模型仅由原始带标注样本统计得到,共进行 $T$ 轮自训练。每轮执行:
- 使用当前模型对全部 $M$ 条未标注样本解码,得到伪真实文本。
- 使用 $N$ 条原始标注样本,加上本轮的 $M$ 条“(OCR,伪真实)”训练对,重新统计模型。
每轮伪标注替换上一轮伪标注,不累积叠加;原始标注始终保留。完成 $T$ 轮后,用最终模型纠正测试 OCR 字符串并输出。
数值稳定性要求:比较候选时必须用整数交叉相乘,避免浮点误差。令
\[N_c=(emit[c][o]+\alpha)(prior[c]+\alpha), \qquad D_c=\sum_{o'}emit[c][o']+\alpha K.\]先验概率的分母对所有候选相同,可以省略。两个候选的比较为
\[\frac{N_1}{D_1}>\frac{N_2}{D_2} \iff N_1D_2>N_2D_1.\]输入描述与约束
第一行四个整数 $N,M,T,\alpha$,依次表示带标注样本数、未标注样本数、迭代轮数和平滑系数。
接下来 $N$ 行,每行两个等长字符串 $ocr_i,truth_i$,前者是 OCR 输出,后者是真实文本。
接下来 $M$ 行,每行一个未标注 OCR 字符串。最后一行是测试 OCR 字符串。
- $1\le N\le 100$。
- $0\le M\le 100$。
- $0\le T\le 20$。
- $1\le\alpha\le 10$。
- 所有字符串仅含小写英文字母,长度满足 $1\le L\le 100$。
- 每个标注对内的两个字符串长度相等,不要求不同样本之间等长。
输出描述
输出一行,表示测试 OCR 字符串经过最终模型纠正后的结果。
样例 1
输入
1 5 2 1
u i
v
v
v
v
v
v
输出
i
解释:标注对是 OCR u、真实字符 i。其后前五行 v 是未标注样本,最后一行 v 是测试串。初始模型对 v 没有发射观测;省略公共分母后,候选 i 的得分为 $2/27$,其余候选为 $1/26$,所以选 i。
第一轮将五个未标注 v 都标为 i,重建后 $emit[i][v]=5$、$prior[i]=6$。第二轮替换伪标注而非再次叠加,最终输出仍为 i。这里的判断同时考虑先验和发射分母,并非只比较先验计数。
样例 2
输入
2 0 0 1
axc abc
axd abd
ax
输出
ab
解释:没有未标注样本,也不进行迭代。训练集中 OCR 字符 x 两次对应真实字符 b,测试串的 x 被纠正为 b,a 保持为 a。
样例 3
输入
2 1 2 1
ax ab
ay ab
az
az
输出
aa
解释:初始模型中真实字符 a、b 都出现两次;对于没有观测过的 z,两者得分均为 $3/28$,高于其他候选,平局选择 a。第一轮把未标注样本 az 纠正为 aa 后重新计数,产生真实 a 被识别为 z 的观测。后续迭代保持这一预测,测试输出 aa。
解题思路:朴素贝叶斯计数与同步迭代
1. 统计方向不能颠倒
对一对 (ocr, truth),逐位置取真实字符 c 和识别字符 o,增加 emit[c][o] 与 prior[c]。再用 total[c] 记录该真实字符的发射总数。
在本题每个真实字符恰好对应一个 OCR 字符,所以 total[c] == prior[c]。代码保留两个数组,使发射分母和先验分子分别对应题面定义,便于核对。
2. 用整数比较候选得分
删去所有候选共有的先验分母后,得分是
\[score(c,o)=\frac{(emit[c][o]+\alpha)(prior[c]+\alpha)}{total[c]+\alpha K}.\]由于 $\alpha>0$,分母始终为正。交叉相乘可以精确判断大于和相等,不需要对数、浮点数或误差阈值。
按 a 到 z 的顺序枚举候选,初始化最优候选为 a,仅在新分数严格更大时替换。相等时保留先遇到的候选,正好实现字典序规则。Python 整数可自动扩展;其他语言实现应使用足够宽的整数,不能直接用 32 位乘积。
3. 每个模型只建立一次字符映射
同一轮内,一个 OCR 字符的预测只由字符本身和当前模型决定,与其位置、所在字符串无关。因此可以先计算
\[table[o]=\text{当前模型对字符 }o\text{ 的最优预测}.\]枚举 $K$ 个识别字符及 $K$ 个真实字符,建表需要 $O(K^2)$ 时间。之后解码任何字符串只需逐字符查表。
4. 所有伪标注必须来自同一个旧模型
先由原始标注建立初始统计量。每轮先固定当前映射表,用它解码全部未标注样本;全部解码完成后,将原始标注和本轮伪标注一起传入计数函数,从零建立新统计量。
不能解码一条就更新模型,让后续样本看到已经改变的统计量;也不能在上一轮统计量上直接累加,否则会重复计算旧伪标注。
完成循环后必须再用最终模型建表,不能直接拿最后一轮用于产生伪标注的旧表输出测试结果。$T=0$ 时直接使用初始模型;$M=0$ 时每轮仍只统计原始标注,模型不变。
正确性证明
引理一:计数函数正确。 每对训练字符串中的每个位置,恰好对题面定义的 $emit[c][o]$、$prior[c]$ 及发射总数贡献一次。遍历所有位置后得到的统计量与定义相同。
引理二:映射表符合解码规则。 省略的先验分母对所有候选相同且为正,不改变得分顺序;交叉相乘保持大小关系。枚举覆盖所有真实字符,只在严格更优时替换,因此最终得到最大分数及其中字典序最小的候选。逐字符查表与逐字符独立解码完全等价。
归纳证明迭代正确。 初始计数由原始标注建立,由引理一正确。假设某轮开始时模型与规定一致,由引理二,本轮统一生成的所有伪标注正确;随后从原始标注和本轮伪标注重新统计,由引理一得到规定的新模型,且没有上一轮伪标注残留。归纳可得完成 $T$ 轮后的模型正确,再由引理二可得测试输出正确。
Python ACM 代码
import sys
K = 26
def build_counts(pairs):
emit = [[0] * K for _ in range(K)]
total = [0] * K
prior = [0] * K
for ocr, truth in pairs:
for observed, actual in zip(ocr, truth):
o = ord(observed) - ord('a')
c = ord(actual) - ord('a')
emit[c][o] += 1
total[c] += 1
prior[c] += 1
return emit, total, prior
def build_table(emit, total, prior, alpha):
table = [0] * K
for o in range(K):
best = 0
best_num = (emit[0][o] + alpha) * (prior[0] + alpha)
best_den = total[0] + alpha * K
for c in range(1, K):
num = (emit[c][o] + alpha) * (prior[c] + alpha)
den = total[c] + alpha * K
if num * best_den > best_num * den:
best = c
best_num = num
best_den = den
table[o] = best
return table
def decode(table, text):
return ''.join(
chr(ord('a') + table[ord(ch) - ord('a')])
for ch in text
)
def self_train(labeled, unlabeled, rounds, alpha):
emit, total, prior = build_counts(labeled)
for _ in range(rounds):
table = build_table(emit, total, prior, alpha)
pseudo = [(ocr, decode(table, ocr)) for ocr in unlabeled]
# 从零统计:仅保留原始标注与本轮伪标注。
emit, total, prior = build_counts(labeled + pseudo)
return build_table(emit, total, prior, alpha)
def main():
tokens = iter(sys.stdin.read().split())
first = next(tokens, None)
if first is None:
return
n = int(first)
m = int(next(tokens))
rounds = int(next(tokens))
alpha = int(next(tokens))
labeled = [(next(tokens), next(tokens)) for _ in range(n)]
unlabeled = [next(tokens) for _ in range(m)]
test = next(tokens)
table = self_train(labeled, unlabeled, rounds, alpha)
print(decode(table, test))
if __name__ == "__main__":
main()
复杂度分析
记 $A$ 为原始标注中 OCR 字符串的总长度,$U$ 为未标注字符串的总长度,$Q$ 为测试串长度,字符集大小 $K=26$。
- 初始计数:$O(A+K^2)$。
- 每轮:建表 $O(K^2)$,生成伪标注 $O(U)$,重新计数 $O(A+U+K^2)$。
- 最终建表与输出:$O(K^2+Q)$。
总时间复杂度为
\[O\bigl(A+K^2+T(A+U+K^2)+Q\bigr).\]以最大字符串长度 $L$ 表示,上界也可写为
\[O\bigl(NL+(T+1)K^2+T(N+M)L+L\bigr).\]这包含 $T=0$ 时仍需执行的初始计数和最终建表,不能把所有预处理都放进乘以 $T$ 的项中。
空间复杂度为 $O(A+U+Q+K^2)$,包括输入字符串、当前轮伪标注和计数表;每轮旧伪标注被替换,不随轮数线性增长。
本题小结
实现重点有四个:emit[真实][识别] 的方向、整数交叉相乘、相等时不替换、每轮重新计数。映射表把同字符的重复预测合并,既降低开销,也便于保证本轮所有伪标注使用同一个模型。
总结
这场编程题不要求调用机器学习库。第一题把 KV Cache 管理抽象为等收益的容量选择,用排序贪心解决;第二题把概率公式落实为计数、精确比较和同步迭代。审题时先检查可行性与状态更新的含义,再处理边界条件,比围绕业务背景搭建复杂系统更直接。