字节跳动

大模型算法三面面经

完整信息

23 条问法 · 19 道题

本篇目录

三面 72 分钟,一行八股都没问。

面试官开场就说,简历我看了,你挑一行,我从这行往里问。我当时还以为这是客气话,挑了个最虚的"负责 Agent 推理链路优化"。然后这个句子被拆了 40 分钟。

体感就一句话,前两面考你会不会,三面考你写的那行字是不是真的。

一、从一句话开始拆

  1. 你说你负责推理链路优化,这条链路上有哪几个环节,各自耗时多少
  2. P99 是多少,为什么是这个数
  3. 你刚才报的 6.8s,拆开给我看

我报的是日均 8 万请求,P50 3.1 秒,P99 6.8 秒。拆开是检索 400ms、首 token 1.9s、工具调用三次合计 2.4s、重排 300ms、剩下的在网络和编排。

他接着问,那你优化的 40% 是从哪来的。

  1. 这个 40% 的分母是什么,含不含失败重试的请求
  2. 失败率多少,失败的请求算不算进 P99
  3. 如果我把 temperature 改成 0,你这个指标会掉多少

第 6 题很阴。温度调到 0 之后输出稳定,缓存命中率会上去,指标变好看,但那是把多样性砍了换来的。我一开始说"会提升",他追问提升多少、代价是什么,我才补上后半句。

二、复杂度现场推

  1. 给定头数 H、序列长度 N、隐藏维 D、层数 L,推整体计算量
  2. N=2048、D=4096 的时候,哪一项是瓶颈
  3. 为什么长上下文要改架构,而不是加卡
  4. KV Cache 的显存怎么估

注意力是 4N²D,FFN 按 FLOPs 是 16ND²,两者相等的临界点是 N = 4D。2048 对 4096,还没到临界点,所以瓶颈在 FFN。他点头,然后说,那 32K 上下文呢。

加卡解决不了,因为显存涨得比算力快。KV Cache 单 token 是 2×L×H×d_head×字节数,按 L=80、H=64、d_head=128、fp16 算,一层一头 32KB,一层 2MB,80 层 160MB,一个 32K 的请求就是 5GB。一张 80G 的卡放不下几个并发。

三、训练那边的事

  1. 预训练 loss 突然起尖刺,你的排查顺序
  2. 数据去重的阈值怎么定
  3. 退火阶段的数据配比你会怎么调
  4. SFT 混多轮对话,loss mask 漏了会怎样

尖刺先看 grad norm,再看 lr 有没有跳,然后定位到具体 batch。很多时候是某条超长样本把 attention 打爆了,或者脏数据里有一段重复的乱码。去重不用拍脑袋定阈值,先算相似度分布,正常是双峰,谷底就是阈值,MinHash 一般落在 0.75 到 0.85 之间。

loss mask 漏了,模型会去学用户的提问方式。表现是上线之后它开始自问自答。

四、对齐与推理

  1. GRPO 的 advantage 为什么能用组内均值,PPO 为什么不行
  2. 训 GRPO 时 loss 一直是 0,正常吗
  3. DAPO 相比 GRPO 动了哪几处
  4. 投机采样的接受率怎么算,接受率低说明什么
  5. PD 分离为什么能提吞吐,代价是什么

loss 恒为 0 基本是两个原因,一组采样全对或者全错,advantage 全 0,梯度就没了;或者 reward 没有区分度,八条回答拿一样的分。前者调难度,后者改奖励。

五、场景题

  1. 长窗口和 RAG,你什么情况下选前者
  2. 你这个东西上线之后,哪个业务指标动了
  3. 只给你一半的卡,你砍哪一块
  4. 反问

第 22 题我答得最实。我会先砍离线评测的并发,再砍重排模型换小号,最后才动生成。因为生成是用户能感知的那一段,砍了就等着被投诉。

手撕:三维积木堆叠

给 n 个长方体,长宽高可以任意旋转,问最多能堆几层,要求下面那块的长和宽都严格大于上面那块。

关键是把"可以旋转"转成"每个块拆成三种朝向",然后按长排序、宽做第二关键字降序,最后在高度上求最长上升子序列。第二关键字必须降序,否则同长不同宽的两块会被算成可以叠。n=1e5 的时候 LIS 要上树状数组,O(n log n)。

def max_stack(boxes):
    b = []
    for x, y, z in boxes:
        for a, c, h in ((x, y, z), (x, z, y), (y, z, x)):
            b.append((a, c, h))
    b.sort(key=lambda t: (t[0], -t[1]))
    hs = sorted({t[2] for t in b})
    bit = [0] * (len(hs) + 1)
    def upd(i, v):
        i += 1
        while i < len(bit):
            bit[i] = max(bit[i], v); i += i & -i
    def qry(i):  # (0, i]
        r = 0
        while i > 0:
            r = max(r, bit[i]); i -= i & -i
        return r
    import bisect
    ans = 0
    for a, c, h in b:
        k = bisect.bisect_left(hs, h)
        cur = qry(k) + h
        upd(k, cur); ans = max(ans, cur)
    return ans

坑有三个。严格大于是 bisect_left 不是 bisect_right,差一个等号就错。同长降序那一行有人写成升序,样本能过,边界过不了。还有旋转,很多人只写两种朝向。

面完出来我在楼下站了五分钟。不是难过,是那种"原来我简历上每一行都得能扛住三个为什么"的后怕。

—— AIGC小白入门记

配图可辨文字

字节跳动校园招聘面试邀请
实习生
字节跳动 ByteDance

本篇目录
  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06
  7. 07
  8. 08
  9. 09
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
    给 n 个长方体,长宽高可以任意旋转,问最多能堆几层,要求下面那块的长和宽都严格大于上面那块。

同作者面经

展开其余 13 篇收起其余 13 篇

相关公司