大厂真题 / 华为

华为 2026-10-14 笔试真题 - AI岗

本场考试概述

考试时间:2026年10月14日

考试岗位:AI岗

难度评级:中等

收录范围:本页收录已披露的 5 道单选题和 2 道编程题。考试概述标注选择题共 20 道,但其余 15 道的题干、选项未披露,因此不补写。

考点分析:

  • 选择题:RMSNorm 的几何意义、浮点相消误差、领域分词、模型后门与 F1 分数。
  • 第一题:CART 分类树、基尼指数、精确分数比较与平局规则(中等)。
  • 第二题:残差融合、掩码归一化、数值边界与格式化输出(简单)。

建议策略:

  • CART 的切分是“等于某值 / 不等于某值”,不能套用连续特征的“小于等于阈值 / 大于阈值”。
  • 平局规则和停止条件同样决定预测结果;用整数交叉相乘比较基尼指数,避免浮点误差。
  • RMSNorm 先核对有效长度、偏置与 padding,再检查分母是否有定义,最后统一格式化。

选择题:已披露的 5 道

1. RMSNorm 对点积的影响

在 LLM 中,常在注意力计算前对特征向量使用 RMSNorm。对向量 $x$ 的操作为 $x/RMS(x)\cdot g$。忽略可学习参数,RMSNorm 对后续的 $q\cdot k$ 点积计算有何本质影响?

  • A. 它强制对向量进行了近似的模长缩放
  • B. 它使得点积在数学上严格等同于欧氏距离
  • C. 它使得点积的结果恒定为 1
  • D. 它改变了向量的夹角,导致原有的空间语义完全反转

答案:A。 忽略稳定项并取非零向量时,

\[RMS(x)=\sqrt{\frac{1}{d}\sum_{i=1}^{d}x_i^2}=\frac{\lVert x\rVert_2}{\sqrt d}.\]

因此 $x/RMS(x)$ 的模长为 $\sqrt d$,方向不变。如果 $q,k$ 都按此方式直接归一化且维度相同,则 $q\cdot k=d\cos\theta$。点积随夹角变化,并非恒为 1,也不等于欧氏距离。实际 RMSNorm 一般带稳定项,模长只是近似固定;若归一化之后还经过不同线性投影,不能直接断言最终 Q、K 的模长仍为 $\sqrt d$。

2. 大数相减的误差控制

计算 $f(x)=\sqrt{x+1}-\sqrt x$ 时,若 $x$ 很大,最有效的误差控制方法是:

  • A. 分子有理化
  • B. 忽略极小项
  • C. 增加计算位数
  • D. 直接计算

答案:A。 两个接近的大数相减会损失有效数字。有理化后得到

\[f(x)=\frac{1}{\sqrt{x+1}+\sqrt x},\]

避免了接近数的直接相减。增加精度能缓解问题,但没有从计算式上消除相消误差。

3. BPE 对领域术语的拆分

在企业级长文本 RAG 系统中,使用 BPE 分词器对中英文混合文档进行 token 化后,发现专业术语、稀有实体、代码变量名被大量拆分。以下哪种做法最直接针对这一问题?

  • A. 改用字符级分词,提升对稀有词汇的表示能力
  • B. 在 tokenizer 中加入领域词表,将专业术语、实体、代码标识符设为完整 token
  • C. 直接增大文档切分长度,抵消 subword 碎片化影响
  • D. 提高向量检索的 top-k,覆盖更多碎片化片段

答案:B。 领域词表能直接减少高频领域术语的子词拆分。字符级分词通常会增加序列长度,调整切块长度或 top-k 则没有改变分词方式。工程中增加 token 还需要同步扩展模型 embedding,并训练或适配新增表示;仅修改 tokenizer 文件并不能保证语义效果。

4. 后门攻击成功的表现

攻击者希望在模型中植入后门,使包含特定“触发器”的输入产生攻击者指定的输出。以下哪种情况最可能表示攻击成功?

  • A. 模型在正常输入上表现良好,但在包含特定罕见字符组合的输入上强制输出特定有害内容
  • B. 模型在所有输入上的准确率都下降了 50%
  • C. 模型训练时间比正常情况增加了 3 倍
  • D. 模型无法处理长度超过 512 token 的输入

答案:A。 后门的典型特征是正常输入下行为正常,出现触发器后才转向预设行为。整体性能下降、训练变慢或长度限制,都不能单独证明后门触发成功。

5. 混淆矩阵与 F1 分数

某分类器在测试集上的混淆矩阵为 TP=40、FP=10、FN=20、TN=30。F1 分数是多少?

  • A. 0.667
  • B. 0.5
  • C. 0.8
  • D. 0.727

答案:D。 精确率 $P=40/(40+10)=0.8$,召回率 $R=40/(40+20)\approx0.667$。

\[F_1=\frac{2PR}{P+R}=\frac{2TP}{2TP+FP+FN}=\frac{80}{110}\approx0.727.\]

TN 不参与 F1 的计算,不能将准确率的分母带入该公式。


第 1 题:基于 CART 分类树预测客户购房选择

题目描述

给定客户训练数据,按指定规则构建 CART 二叉分类树,预测待测客户是否购房。每条训练记录包含 4 个整数枚举特征和 1 个二分类标签:

  • 特征 1:年龄段,0 为 18—30 岁,1 为 31—40 岁,2 为 41—55 岁,3 为 56 岁及以上。
  • 特征 2:婚姻状况,0 为单身,1 为恋爱,2 为已婚。
  • 特征 3:是否有房,0 为无,1 为有。
  • 特征 4:是否有工作,0 为无,1 为有。
  • 标签:是否购房,0 为否,1 为是。

对特征 $A$ 和候选取值 $v$,左子集为 $D_1={s\in D:s_A=v}$,右子集为 $D_2={s\in D:s_A\ne v}$。候选值取当前节点出现的特征值。

对于包含 $K$ 个类别的数据集 $D$,第 $k$ 类占比为 $p_k$,基尼指数为

\[Gini(D)=1-\sum_{k=1}^{K}p_k^2.\]

切分的加权基尼指数为

\[Gini(D,A,v)=\frac{\lvert D_1\rvert}{\lvert D\rvert}Gini(D_1)+\frac{\lvert D_2\rvert}{\lvert D\rvert}Gini(D_2).\]

选择加权基尼指数最小的切分。多个候选同分时先选特征编号较小的;同一特征同分时选取值较小的。满足以下任一条件即停止,生成叶子:

  1. 所有样本标签相同。
  2. 样本数小于 2。
  3. 没有切分能够使基尼指数严格降低。

叶子预测多数类别;类别数相同时预测编号较小的 0。预测时特征值等于切分值走左支,否则走右支。

输入格式:第一行为 $N$,$4\le N\le20$;随后 $N$ 行各含 5 个整数,前 4 个为特征,最后一个为标签。再输入 $M$,$1\le M\le10$,随后 $M$ 行各含 4 个测试特征。

输出格式:输出一行 $M$ 个预测标签,用空格分隔。

样例 1

输入

8
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 0 1
2 2 1 1 1
2 0 0 0 0
3 2 1 0 0
3 2 0 1 1
3
2 0 0 0
1 1 1 0
3 2 1 0

输出

0 1 0

样例 2

输入

20
0 0 0 1 1
0 0 1 0 0
0 1 0 0 0
0 1 1 1 1
0 2 0 1 1
1 0 0 0 0
1 0 1 1 1
1 1 0 1 1
1 1 1 0 0
1 2 1 0 0
2 0 0 1 1
2 0 1 0 1
2 1 0 0 0
2 1 1 1 1
2 2 0 0 0
3 0 0 0 0
3 0 1 1 0
3 1 0 1 1
3 1 1 0 0
3 2 0 1 0
10
1 1 1 0
2 0 0 1
3 2 0 1
0 0 0 1
1 2 1 0
2 1 0 0
3 0 1 1
0 1 1 1
1 0 0 0
2 2 0 0

输出

0 1 0 1 0 0 0 1 0 0

思路分析

第一步:把训练过程视为确定性的递归规则。

本题不是寻找全局最优决策树,也不进行剪枝或随机选特征。对每个节点枚举规定的二元切分,选择当前最优,再递归处理左右子集即可。某个特征可以重复使用,因为先分出一种取值后,右侧仍可能包含该特征的其他取值。

第二步:用分数精确比较基尼指数。

设节点中标签 0、1 的数量分别为 $c_0,c_1$,样本数为 $s=c_0+c_1$。定义

\[mass(S)=\lvert S\rvert\cdot Gini(S)=\frac{s^2-c_0^2-c_1^2}{s}=\frac{2c_0c_1}{s},\]

空集的 mass 约定为 0。对于同一个父节点,所有候选的加权基尼都有相同的分母 $\lvert D\rvert$,所以可以比较

\[mass(D_1)+mass(D_2).\]

代码保存分数的分子和分母,不需要浮点运算。比较 $a/b<c/d$ 时判断 $ad<cb$;两分数相加使用 $(ad+cb)/(bd)$。这样不会把数学上的平局误判成微小差异。

第三步:用遍历顺序落实平局规则。

按特征 1 到 4、取值从小到大枚举,只在新分数严格更小时更新最优项。同分候选不覆盖旧候选,自然保留较小特征编号和较小取值。

最优切分仍必须严格优于父节点的 mass,否则直接返回多数类叶子。全体样本落到一侧的候选不会降低基尼,可以跳过。标签不纯也可能停止:例如异或型标签,两侧类别比例与父节点一致时,任何一步都不能降低基尼;不能为了以后可能变纯而强行继续切分。

第四步:核对样例与预测路径。

样例 1 的根节点标签数量为 3 个 0、5 个 1,基尼为 0.46875。以“特征 4 等于 0”切分,左侧标签为 0、1、0、0,右侧全为 1,加权基尼为 0.1875。左右互换的取值 1 得到同分,因此选取值 0。左侧再按“特征 1 等于 1”切分,两侧变纯。三个测试客户最终预测为 0、1、0。

训练时没有出现的测试取值,也按“不等于当前切分值”走右支,不需要单独建立未知类别分支。

题解代码

import sys
input = sys.stdin.readline


def mass(count_zero, count_one):
    size = count_zero + count_one
    return (2 * count_zero * count_one, size) if size else (0, 1)


def add_fraction(a, b):
    return (a[0] * b[1] + b[0] * a[1], a[1] * b[1])


def less_fraction(a, b):
    return a[0] * b[1] < b[0] * a[1]


def build_tree(rows):
    count_one = sum(row[4] for row in rows)
    count_zero = len(rows) - count_one
    majority = int(count_one > count_zero)
    if count_zero == 0 or count_one == 0 or len(rows) < 2:
        return (majority,)

    best_score = mass(count_zero, count_one)
    best_feature = best_value = None
    for feature in range(4):
        for value in sorted({row[feature] for row in rows}):
            left = [row for row in rows if row[feature] == value]
            left_one = sum(row[4] for row in left)
            left_zero = len(left) - left_one
            score = add_fraction(
                mass(left_zero, left_one),
                mass(count_zero - left_zero, count_one - left_one),
            )
            if less_fraction(score, best_score):
                best_score = score
                best_feature, best_value = feature, value

    if best_feature is None:
        return (majority,)
    left = [row for row in rows if row[best_feature] == best_value]
    right = [row for row in rows if row[best_feature] != best_value]
    return (best_feature, best_value, build_tree(left), build_tree(right))


def predict(tree, sample):
    while len(tree) != 1:
        feature, value, left, right = tree
        tree = left if sample[feature] == value else right
    return tree[0]


def solve():
    n = int(input())
    rows = [list(map(int, input().split())) for _ in range(n)]
    tree = build_tree(rows)
    m = int(input())
    answers = [predict(tree, list(map(int, input().split()))) for _ in range(m)]
    print(*answers)


if __name__ == "__main__":
    solve()

复杂度分析

设树深为 $H$。每个特征的取值数不超过 4,节点中有 $s$ 条记录时,枚举并计算全部候选需要 $O(s)$ 时间。每层节点的样本集合互不重叠,建树需要 $O(NH)$,预测需要 $O(MH)$。

本题的取值域固定为 4、3、2、2。沿任一路径,每次有效切分都会使某个特征的剩余可能取值数至少减少 1,因此 $H\le(4-1)+(3-1)+(2-1)+(2-1)=7$。按本题固定域,时间复杂度为 $O(N+M)$,常数来自特征与候选枚举。如果只使用 $H\le N$ 而不利用固定域带来的更紧深度界,也可写成保守上界 $O(N^2+MN)$;这一估计仍以每个特征的候选数为常数为前提。

空间复杂度:递归会保留祖先的样本列表,上界为 $O(NH)$;本题 $H\le7$,因此为 $O(N)$。树节点、训练集和输出列表同样在此范围内。


第 2 题:轻量化推理引擎中的 Masked Fused RMSNorm

题目描述

实现融合残差连接、RMSNorm、逐元素缩放、偏置和 padding mask 的算子。共有 $m$ 个样本,向量最大长度为 $n$;所有样本共享缩放向量 $\gamma$、偏置向量 $\beta$ 和稳定项 $\varepsilon$。第 $k$ 个样本给出有效长度 $l_k$、残差系数 $\alpha_k$、输入向量 $x_k$ 和残差向量 $r_k$。

先融合残差:

\[h_{k,i}=x_{k,i}+\alpha_k\cdot r_{k,i}.\]

只对前 $l_k$ 个有效位置统计能量与均方根:

\[energy_k=\sum_{i=1}^{l_k}h_{k,i}^2,\qquad d_k=\max(l_k,1),\qquad RMS_k=\sqrt{\frac{energy_k}{d_k}+\varepsilon}.\]

有效位置缩放并加偏置;padding 位置固定为 0:

\[y_{k,i}=\begin{cases} \gamma_i\cdot h_{k,i}/RMS_k+\beta_i, & 1\le i\le l_k,\\ 0, & l_k<i\le n. \end{cases}\]

输入格式与约束:

  • 第一行为 m n epsilon,$1\le m\le200$,$1\le n\le256$,$0\le\varepsilon\le1$。
  • 第二、三行各有 $n$ 个浮点数,分别为 $\gamma$ 和 $\beta$,每项范围为 $[-10^3,10^3]$。
  • 接下来 $m$ 行,每行格式为 l alpha x_1 ... x_n r_1 ... r_n。
  • $0\le l\le n$,$-10^3\le\alpha\le10^3$;$x_i,r_i$ 均在 $[-10^6,10^6]$ 内。
  • 时间限制:C/C++ 1000ms,其他语言 2000ms;内存限制:C/C++ 512MB,其他语言 1024MB。

输出格式:每个样本输出一行 y_1 ... y_n | RMS | energy。各项之间单个空格,竖线两侧各一个空格。所有数保留两位小数,按标准四舍五入;绝对值小于 0.005 时统一输出 0.00,避免 -0.00。

题面边界缺口:约束允许 $\varepsilon=0$。若 $l>0$ 且全部有效 $h_i=0$,则 $RMS=0$,有效位置公式出现 $0/0$,题面没有规定结果。下方实现只对公式有定义的输入给出答案;遇到这类未定义输入会明确报错,不擅自把输出规定为 0 或偏置。$l=0$ 时不执行有效位置除法,因此即使 $\varepsilon=0$ 仍有定义。

样例 1

输入

2 3 0.01
1.0 0.5 -1.0
0.0 1.0 0.0
3 1.0 3.0 4.0 0.0 0.0 0.0 0.0
2 0.5 -1.0 1.0 -1.0 2.0 -2.0 10.0

输出

1.04 1.69 0.00 | 2.89 | 25.00
0.00 1.00 0.00 | 0.10 | 0.00

样例 2

输入

2 4 0.000001
1.0 1.0 1.0 1.0
0.0 0.0 0.0 0.0
4 0.0 1.0 2.0 3.0 4.0 0.0 0.0 0.0 0.0
0 1.0 0.0 0.0 0.0 0.0 1.0 2.0 3.0 4.0

输出

0.37 0.73 1.10 1.46 | 2.74 | 30.00
0.00 0.00 0.00 0.00 | 0.00 | 0.00

思路分析

第一步:确定掩码作用范围。

padding 不参与平方和,输出也不加偏置。可以只计算前 $l$ 个位置的融合值,并将完整输出向量初始化为 0。共享参数按位置索引,不能把缩放向量 $\gamma$ 与样本残差向量 $r$ 混淆。

第二步:按依赖顺序完成计算。

先得到有效位置的 $h$,然后累加 $energy$,再求 $RMS$,最后计算各个有效位置的 $y$。均方的分母是 $\max(l,1)$,不是向量最大长度 $n$;稳定项位于平方根内部。不能先把 RMS 舍入到两位再计算输出,只在最终展示时舍入。

当 $l=0$,平方和为 0,RMS 为 $\sqrt\varepsilon$,输出向量全零。样例 2 的第二个样本 RMS 实际为 0.001,只是打印后显示 0.00。

样例 1 的第一个样本得到 $h=[3,4,0]$、$energy=25$、$RMS=\sqrt{25/3+0.01}$。第二个样本完整融合值为 $[0,0,4]$,最后一个位置是 padding,因此平方和仍为 0;第二个有效位置的输出为偏置 1。

第三步:处理四舍五入和负零。

Python 的 f"{value:.2f}" 在精确中点采用 ties-to-even,并不总等价于题面所写的标准四舍五入。例如精确可表示的 0.125,前者输出 0.12,通常的四舍五入应为 0.13。这里用 Decimal.from_float 保留已经计算出的二进制浮点值,再用 ROUND_HALF_UP 定标到两位小数;计算过程仍使用双精度,最后一步才格式化。绝对值小于 0.005 的值先直接输出 0.00。

题面没有提供恰好半分值的判题样例或特殊容差,因而无法确认隐藏判题是否也严格采用 HALF_UP;本实现按文字要求处理,而不将 ties-to-even 当作同一种规则。二进制浮点值与理想十进制实数之间仍可能有微小差异,这不是增加打印位数能够完全消除的。

题解代码

import math
import sys
from decimal import Decimal, ROUND_HALF_UP
input = sys.stdin.readline


def format_value(value):
    if abs(value) < 0.005:
        return "0.00"
    rounded = Decimal.from_float(value).quantize(
        Decimal("0.01"), rounding=ROUND_HALF_UP
    )
    return format(rounded, ".2f")


def masked_rms_norm(length, alpha, x, residual, gamma, beta, epsilon):
    fused = [x[i] + alpha * residual[i] for i in range(length)]
    energy = sum(value * value for value in fused)
    rms = math.sqrt(energy / max(length, 1) + epsilon)
    if length > 0 and rms == 0.0:
        raise ValueError("题面未定义:有效长度非零且 RMS 为零")
    output = [0.0] * len(x)
    for i in range(length):
        output[i] = gamma[i] * (fused[i] / rms) + beta[i]
    return output, rms, energy


def solve():
    first = input().split()
    m, n = int(first[0]), int(first[1])
    epsilon = float(first[2])
    gamma = list(map(float, input().split()))
    beta = list(map(float, input().split()))
    for _ in range(m):
        tokens = input().split()
        length, alpha = int(tokens[0]), float(tokens[1])
        x = list(map(float, tokens[2:2 + n]))
        residual = list(map(float, tokens[2 + n:2 + 2 * n]))
        output, rms, energy = masked_rms_norm(
            length, alpha, x, residual, gamma, beta, epsilon
        )
        print(" ".join(format_value(value) for value in output)
              + " | " + format_value(rms) + " | " + format_value(energy))


if __name__ == "__main__":
    solve()

复杂度分析

时间复杂度:$O(mn)$。每个样本读取长度为 $n$ 的向量,计算有效位置,并输出 $n$ 个结果;题面数值范围有限,格式化每个数的开销可视为常数。

空间复杂度:$O(n)$。逐行处理样本,只保留共享向量和当前样本的输入、有效融合值、输出,不保存全部样本。


小结

  • RMSNorm 的缩放与方向、F1 的分母、相消误差的计算式,都需要从定义出发判断。
  • CART 是一套确定性规则:等值切分、严格降低基尼、两级平局优先级,缺少任何一条都会改变结果。
  • 融合归一化要把有效位统计、padding 输出和最终舍入分开处理,避免提前舍入或给 padding 加偏置。
  • 本页保留实际披露范围;RMS 为零的未定义边界与中点舍入判题口径仍需题面补充,不能据样例通过宣称所有允许输入均有确定答案。