一面

2021 年 7 月 29 日晚 20:00,只问了 38 分钟就结束了(捂脸。面试官人很好,很友善的样子,然后问的也都比较简单,都是我简历范围内的东西,也没深挖,所以结束的也很快。

首先面试官介绍了下 AML 部门(第一次面试紧张没咋听进去他在讲啥,就回答了个嗯嗯。。。)

然后是自我介绍:我叫 xxx,来自浙江,现在是xx学校研一的学生。我本科和研究生都是xx学校计算机科学与技术专业的。希望有机会来字节实习。然后我的项目主要是简历上写的天池的比赛,xxx分类,用到了数据增强、半监督学习(伪标签)、知识蒸馏、模型融合等技术。然后我还学过 GBDT、XGBoost 和 Transformer 等,对这些有一定的了解。以上就是我的自我介绍了。

面试官还问了我的导师是谁(捂脸,不知道是不是校友。

然后开始问简历上的项目

  • 你写的这个双图网络是什么?
  • 数据增强用了哪些方法?
  • 这个伪标签是什么意思?
  • 知识蒸馏从 Resnet34 到 Resnet18,训练时间从多少减少到多少了吗?

    回答说这个具体的运行时间不太记得了,但是评估指标 F1 score 提升了

  • 这个模型变小了为什么评估指标会提升呢?

  • F1 score 怎么求?精确度和召回率怎么求?这个混淆矩阵的行和列的 N、P 分别代表什么呢?

    在代码框里简单画了个混淆矩阵,然后说了下公式

  • 这个投票机制是怎么做的呢?哦,只是对结果做了融合,没有针对模型是把?

  • 这个是分类问题还是回归问题呢?
  • 分类问题用的损失函数是什么呢?

    交叉熵损失函数

  • 介绍一下交叉熵损失函数

    写了下公式,多分类的交叉熵

  • 二分类的交叉熵呢?

  • XGBoost 这个广告分类器是干什么?数据集是哪里找的,特征有哪些?

  • 介绍一些贝叶斯优化?
  • 介绍一些 XGBoost
  • 决策树怎么构建呢?
  • 决策树怎么停止生长?
  • AUC 怎么算?FPR、TPR 怎么算?

    讲了 AUC 是 ROC 曲线下的面积,横轴是 FPR,纵轴是 TPR。又问 FPR、TPR 怎么算?

  • Transformer 的注意力机制介绍一下?

    反问了下是讲注意力机制还是自注意力机制,面试官让讲自注意力机制

  • 注意力机制和自注意力机制有什么区别吗?

    答:本质上其实是一样的,只不过注意力是关注其它序列的上下文资讯,自注意力是关注自身序列的上下文资讯

其它还有什么问的记不清了

算法题

  • 最大公约数

    感觉是大一就做过的题,应该是简单题,但我想不起来怎么做了,问能不能先暴力做,暴力昨晚跑了下测试样例应该没问题。面试官问怎么改进呢,没想出来,问面试官能不能给点提示,不知道是不是面试官也不知道,说没事换下一道题。。。

  • 判断是否是合法二叉搜索树?

    递归收缩上下界做的。问面试官要不要建个树跑一下,面试官说太麻烦了不用了,然后我讲了下我写的意思,就过了

最后,反问环节

  • 问了面试官平时工作都做些什么

    面试官说这个问题很好,讲了一下,巴拉巴拉

  • 问面试官觉得我的表现怎么样,有什么不足和需要改进的地方?

    面试官说还不错。问我平时用各种工具有没有深究? 我说我第一道算法题应该是简单题,但我忘了它应该怎么做了,只能用暴力解。 面试官说有些东西不记得了也正常,暴力做出来也可以

二面凉

7.30 下午 16:00 二面,总共面了 50 多分钟,问的问题比一面深不少,但感觉其实问的也不算很难吧,只是有些问题我确实不会,还是自己太菜了。

上来先做算法题

  • 给定两个单调非减的链表头节点,按照单调增(去重)的顺序【打印】出链表每个节点的 val

    • 单调非减意味着可能会有重复值
    • 只要打印出来,类似于遍历,不要改原始链表
      1. struct Node {
      2. int val;
      3. Node* next;
      4. }
      5. void merge_print(Node* l1, Node* l2)
      6. {
      7. }

      其实很简单的一道题,但是太粗心了,为了去重用了一个 pre 存之间的值,然后没考虑链表头节点为空的情况,感觉平时写题也会考虑一下特殊情况的,面试可能太紧张了大意了。面试官提示了之后处理了下头节点为空的情况,然后忘记了处理完特殊情况应该直接 return!!!最后面试官反馈说代码题 bug 太多,没有直接做到 bug free,挺减分的。。。

  • 求第 k 大的数组,不用写代码,只用说思路

    1. int topk(std::vector<int> &nums, size_t k)

    说了下用快速选择做,问了时间复杂度,只知道是 O(N) 的,但是之前看分析求这个复杂度挺难的样子就没看,问怎么算的我不会。。。然后问了快速排序的时间复杂度,O(nlogn)怎么求,答每次找切分点,两边再分别排序,所以相当于二叉树,深度为 logn,每次求切分点遍历数组时间复杂度 O(n),所以是 nlogn

  • 扩展:数组是 const,也就是数组不能改动,没法排序,也不能复制到其他数组,要求空间复杂度 O(1),怎么做?

    1. int const_topk(const std::vector<int> &nums, size_t k)

    面试官说这是扩展题,答不上来也没关系,问有没有思路。想不出来。。。 实际上应该用二分去做,解答参见:求 const top k

  • 问题:以下两种写法哪个更好?(两个 for 循环 vs 一个 for 循环)

    1. std::vector<int> a(4096), b(4096);
    2. do_some_calc(&a, &b);
    3. // No.1
    4. int sum = 0;
    5. for (size_t idx = 0; idx < 4096; ++idx) {
    6. sum += a[idx];
    7. sum += b[idx];
    8. }
    9. // No.2
    10. int sum = 0;
    11. for (size_t idx = 0; idx < 4096; ++idx) sum += a[idx];
    12. for (size_t idx = 0; idx < 4096; ++idx) sum += b[idx];

    面试的时候不会,没答上来 No.2 使用双数组遍历(两个 for 循环)会更快,因为 L1cache?网上没找到确切的答案

大概分析:(局部性) 参考:为什么二维数组中行遍历比列遍历快很多?
假设 cache 的大小为 4*16 字节
当第一次访问 a[0] 时,会将 a[0] ~ a[3] 这连续的 16 个字节读进 cache 的第一行,并造成一次 cache miss(访问 a[0] 的时候 cache 里没有)。当前 cache 如下:

a[0] miss a[1] hit a[2] hit a[3] hit

那如果使用 No.2 的方法,接下去访问 a[1],a[2],a[3] 都命中,cache 命中率为 75%,后续每 4 个元素都是这样

而如果使用 No.1 的方法,访问 a[0] 后访问 b[0],cache 中没有需加载,之后访问 a[1],b[1]~a[3],b[3] 都命中,所以命中率也是 75%???

a[0] miss a[1] hit a[2] hit a[3] hit
b[0] miss b[1] hit b[2] hit b[3] hit
a[4] miss a[5] hit a[6] hit a[7] hit
b[4] miss b[5] hit b[6] hit b[7] hit

然后是问基础知识
先是关于天池太阳黑子分类的项目的问题,先让介绍了这个项目

  • 使用知识蒸馏是这个比赛对运行的时间有要求吗?

    不是出于时间的考量,比赛的评估指标就是 F1 score,因此就是要提高 F1 score 的值。 之所以想到用知识蒸馏,是因为知识蒸馏同时使用了 soft loss 和 hard loss soft loss:使用带温度 T 的 softmax,输出的各类别的概率更加平滑,相对于原本的 softmax,能学习到类间的相似性,相当于 label 层面的数据增强 hard loss:使用 ground truth 一定程度上能抵消伪标签对教师模型带来的负面影响

  • 为什么教师模型选择 resnet34,学生模型选择 resnet18?

    实验对比表明效果好

  • 那为什么不用 resnet34 本身作为学生模型呢?

    实验发现效果没 resnet18 好。又追问了为什么 resnet34 作为学生模型不如 resnet 18?没答上来 按反馈,为什么用 resnet18 蒸馏 resnet34 这个问题没能做出原理解释性的回答,面试官不满意

  • 知识蒸馏过程中还有用到哪些 tricks?

    温度 T ,实验后发现 T 设为 10 比较合适 还有两个 loss 加权的权值 α,实验后发现 soft loss 的权值设为 0.95 比较好

  • 为什么伪标签这里没有考虑 soft?

    因为当时先用了伪标签来提升效果,也没想到后面会用知识蒸馏做,也就没想到用 soft,就伪标签直接做了

  • 介绍一些用了哪些数据增强的方法?

  • 用的 backbone 是预训练还是从头训练效果好?
  • 使用预训练的 resnet34 进行微调的时候,调了几层(固定了哪些层的参数)?

    没有固定,但是最后的全连接层 fc 的学习率设置为前面预训练过部分的 10 倍,从头训练。 初始学习率为 0.01(fc 初始 0.1),然后使用余弦学习率衰减,周期为 20 个 epoch,最小学习率为 5e-6

  • 数据集的大小?

然后让介绍第二个项目:XGBoost 广告分类器

  • 特征数有多少?
  • 有做特征的处理吗?
  • 介绍一下 GBDT
  • GBDT 的决策树怎么做二分类?
  • 用决策树做分类,最终的分是怎么合起来的,每个决策树都划到一个叶子节点,最后怎么判断到底属于哪个类别呢?分类时候的残差?

    答不上来,说了加权,被反驳了,然后后来面试官的反馈说我对 GBDT 这块根本没理解。。。

  • 写一下 logloss

    就是交叉熵损失,写了二分类交叉熵损失函数的公式

  • 为什么取 log

    答:二分类用极大似然估计(MLE)推导的时候,似然函数是对所有训练集样本的概率的连乘,那么两边取 log 就变成了和的形式

  • logloss 和最大似然之间的关系

    写了一下逻辑回归用极大似然估计推导出交叉熵损失的过程

  • 为什么是所有训练集样本的概率相乘呢?

    又追问了为什么可以相乘,回答大概是样本之间独立?了解的不清楚。然后后来面试官反馈里有一条就说我对最大似然估计的了解不够。。。

  • SGD,先介绍一下,再写一下公式

    狭义上就是用单个样本的损失函数来近似目标函数,广义上小批量梯度下降法就是用m个训练样本损失函数的均值来近似目标函数 然后写了下梯度下降更新参数的公式 字节 AML 算法岗日常实习凉经 - 图1

  • 为什么是减去梯度?

    答:用负梯度信息更细参数;追问:为什么用负梯度信息?没答上来。。。

  • 对 RNN 了解吗?

    不怎么了解

  • 最大/均值池化等(max/sum_pooling/vec * scalar/vec_a * vec_b)的梯度计算(还是啥问题记不清了),我不会

  • 梯度反向传播?

    用链式法则来计算梯度

  • 那为什么不可以直接去求第一层的梯度?

    答:应该没法计算吧?要用链式法则连乘的形式来计算导数

  • 为什么会产生梯度消失问题?

  • 有什么方法可以缓解梯度消失问题?

    残差连接、ReLU 激活函数

反问环节:

  • 问了实习日常都做什么

    面试官很认真的回答了,我还问了那边做的是推荐算法吧,如果没有学过相关的是不是也能跟着做,面试官说算法这些学的还是快的。总之回答的挺认真的,就搞得我还以为自己挺有戏的,结果就挂了。。。

  • 问了这场面试我的不足和要改进的地方

    • 一开始的算法没有 bug free 很减分(语言是门艺术,面试官说没有 bug free 原来是委婉的说法hhh,后来的面试反馈说代码题 bug 太多了。。。)
    • GBDT 分类预测怎么做那块