一面
2022.03.17 周四晚 18.00-19.00+
自我介绍
针对住小帮推荐算法实习经历提问:
- 看你做的主要是模型压缩的工作?
- 也不是,但确实模型参数量比较大,所以做了特征选择的工作来压缩模型参数量
- 样本量?
- 每天的样本在 1000w 左右(包含正、负样本)
- 模型是实时更新的吗,还是天级?
- 上线之后是会实时更新的
- 简历上写的验证 share bottom 各部分 tower 重要性,是怎么做的?
- 比较直接,去掉对应的 tower 后重训,对比 auc 变化。去掉 deep nn tower 后 auc 反而略涨了 0.02%,说明 deep nn tower 效果不好,需要优化
- share 指的是?
- MTL 模型,有 feed 的 CTR、push 的 CTR,以及其它几个场景的 CTR,顶部各个任务分别有个 dense tower 输出,下面则是共享的 share bottom,share bottom 里又分为几个不同的结构,比如 deep nn tower、DFM tower、LR
- NAS 搜 emb size 怎么做的
- 先讲了 NAS 的原理,搜索空间、搜索策略(可微架构搜索 DARTS,softmax 将离散搜索空间变为连续搜索空间)、评估策略(one-shot learning,超图)
- 然后讲了 NAS 搜 emb size 的原理,在代码框写了下大概情况
- 阈值如何确定?
- 训练对比 AUC
- 这个 NAS 就是做的特征选择,没有改网络结构是吧?
- 这是 MTL 的模型,给 feed 进行特征选择和 size 选择,不会影响其它 label 吗?
- 当时任务就是优化 feed 的指标
- 训练 NAS 用了多久的数据训练?
- 几个月(当时忘了,实际预训练应该是训了一个月的数据)
- 除了 NAS,还有没有用过别的特征选择方法
- 说了下 NAS 相比 slot 级选择的优点。然后讲了简历上写的 feature insight 方法,根据 embedding 输入 NN 后第一层输出的 L2 范数,经验性的做法
- 怎么理解的?上面用 NN 第一层输出的 L2 范数来衡量特征重要性?
- 又讲了下后面用的 attention fm 筛选交叉项,就是用权重代表交叉项的重要性
- Adam 优化算法
- 同时使用一阶动量 + 二阶动量,写了下 Adam 的公式
- 两个特征的 embedding,用 Adam 算法训练,一个更新 1w次,一个更新 100w 次,更新后两个特征 embedding 的 L2 范数还一样吗?
- AUC 的定义
- 说了几何意义是 ROC 去线下的面积;又说了下概率意义
- AUC 是 ROC 去线下的面积,ROC 的坐标 (0, 1) 代表什么?
- 面试的时候理解错了,以为说是从原点沿 y 轴往上走了一格这个点代表什么。实际上 y 轴坐标最大值就是 1,因此 (0, 1)应该说明全部正样本都排在负样本前面,AUC 为 1
- ROC曲线: 完美的预测是一个在左上角的点,在ROC空间座标 (0,1)点,X=0 代表着没有伪阳性,Y=1 代表着没有伪阴性(所有的阳性都是真阳性);也就是说,不管分类器输出结果是阳性或阴性,都是100%正确。一个随机的预测会得到位于从 (0, 0) 到 (1, 1) 对角线(也叫无识别率线)上的一个点;最直观的随机预测的例子就是抛硬币。
- 简历上写了一个你自己实现的类似 MMOE 的 MTL 结构?
- 很实诚地说了不是自己实现的,公司里也有人用。。。。。。
- 了解 MMOE 吗?
- deep nn tower 里的激活函数?
- 默认是 ReLU
- ReLU 激活函数的优缺点?
- 优点:防止梯度消失(>0 梯度一直为 1)
- 缺点:神经元死亡,一旦小于 0,就一直为 0
- 平时用什么语言?
- ++
- static ?
算法题:
- 92. 反转链表 II:翻转链表区间第 m 到第 n 个节点
- 要自己构造链表节点结构,最后还要自己构建链表来测试一下用例
- 知道是动态规划解,但是一时间想不起来状态转移方程是怎样的了,就说了用递归回溯解
- 面试官问了回溯到时间复杂度,不好准确判定,但是每种组合最多需要 target 枚硬币,每个位置 n 种选择,因此上限
,但实际不会那么大
- 面试官又问有没有减小时间复杂度的方法——动态规划
- 想了下,说 dp[i] 代表结果,状态转移方程大概是
,当然 i-coin 要大于等于 0
- 面试官说大概思路是对的,还有些细节要考虑(就是要外循环先遍历硬币,在内循环遍历总金额,否则会重复统计),面试时间也差不多了,就不写了
反问:
- 你们那边日常的主要工作是做什么?
- 暑期转正的要求是什么,机制是咋样的,6 月实习的话对转正来说晚吗?
- 这次面试的结果大概什么时候能通知到我呢?
二面
2022.03.23 周三晚 20.00-21.00,忘了录音了,记录了自己还记得的内容
自我介绍
针对简历的项目提问:(只问了不到 20 分钟)
- 看你实习主要做的都是模型压缩的工作?
- 确实做了不少特征选择的工作
- 说说你都是怎么做特征选择的?
- 讲了下 NAS 搜 emb size。
- 面试官对 NAS 不是很懂,问一般 NAS 搜索空间都是离散的,用强化学习等方法做,你这个是怎么能变成连续的,是因为搜 emb size 是特殊情况吗?讲了下 DARTS 可微架构搜索,给每个子结构一个权重 w,再通过 softmax 得到概率分布 a,a 就和模型参数一样,都可以通过梯度下降法学习得到
- 看你的模型是 MTL 的,你了解 MTL 模型吗,比如 MMOE 和 PLE
- 讲了下share bottom,mmoe,ple 的发展及问题,以及我简历上写的改进
- AUC 的定义
- 说了下几何定义和概率含义
- 问根据概率含义,怎么求 AUC
- 讲了下暴力解法,两层循环统计出正序对个数,除以总对数
- AUC 会受正负样本比例变化影响吗?
- 不太会,因为 AUC 是 ROC 曲线下的面积,横轴 FPR 只和负样本有关,纵轴 TPR 只和正样本有关
- 那 PR 曲线会受正负样本比例变化的影响吗?
- 会,因为纵轴精确率 P = TP/(TP+FP),同时包含正负样本
- 多分类通常会用 softmax 激活函数,如果 softmax 带温度 T>1,那么它输出的分布,相对于不带温度 T 的 softmax(T=1)会熵增还是熵减呢?
- 说了自己对熵的定义记不太清楚了,但知道 softmax 带温度 T>1,那么输出会更平滑
- 面试官说如果各个类别的输出更接近不好区分,那么就是混沌的,熵增的;如果各个类别的输出区分性更大,那么就是熵减的。我说那应该是熵增
概率题:A、 B ~ U(0, 1],求 max(A, B) 期望
- 概率本科学的不太记得了,就明说了,然后就开始做算法题
算法题:
给定前序遍历序列和后序遍历数组,构造二叉树,然后按层序打印。
- 889. 根据前序和后序遍历构造二叉树:给定两个整数数组,
preorder和postorder,其中preorder是一个具有 无重复 值的二叉树的前序遍历,postorder是同一棵树的后序遍历,重构并返回二叉树。
如果存在多个答案,您可以返回其中 任何 一个。Input: pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1]Output: [1,2,3,4,5,6,7]
struct TreeNode { int val; TreeNode left; TreeNode right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}TreeNode(int value) : val(value), left(nullptr), right(nullptr) {}
};
TreeNode* dfs(vector
int value = preOrder[preLeft];TreeNode* root = new TreeNode(value);// 注意这里,如果只有一个节点,要直接返回这个节点,否则下面招左子树第一个节点,// preLeft+1可能越界if(preLeft == preRight)return root;int leftFirstVal = preOrder[preLeft + 1];int leftIdxInPost = postMap[leftFirstVal];int leftLen = leftIdxInPost - postLeft + 1;root->left = dfs(preOrder, postMap, preLeft + 1, preLeft + leftLen, postLeft, leftIdxInPost);root->right = dfs(preOrder, postMap, preLeft + leftLen + 1, preRight, leftIdxInPost + 1, postRight);return root;
}
void printTree(TreeNode* root) { if(root == nullptr) return;
queue<TreeNode*> nodeQ;nodeQ.push(root);while(!nodeQ.empty()){int levelCnt = nodeQ.size();for(int i = 0; i < levelCnt; ++i){TreeNode* cur = nodeQ.front();nodeQ.pop();printf("%d ", cur->val);if(cur->left != nullptr)nodeQ.push(cur->left);if(cur->right != nullptr)nodeQ.push(cur->right);}printf("\n");}
}
int main() {
vector
{
postMap[postOrder[i]] = i;
}
TreeNode* root = dfs(preOrder, postMap, 0, preOrder.size() - 1, 0, postOrder.size() - 1);// cout << root->val << endl;printf("%d\n", root->val);printTree(root);return 0;
} ```
算法题做的一波三折:
- 看到先序和后序序列,开始怀疑两个序列不包括中序,能否还原二叉树,问了面试官,说可能存在多种可能,返回任意一个即可。
- 存在多种可能,想是否要递归回溯判断各种可能可不可行。面试官说最简单的,假设每个根节点都存在左子树即可。(当然有别的可能,根节点可能没有左子树,剩余序列都是右子树,这里不管)
- 还是懵住了,没想到怎么确定左子树的长度,面试官提示问我,先序遍历顺序是根左右、后序遍历顺序是左右根,所以先序序列的根节点右边是左子树第一个节点,找到其在后序遍历的位置,就是后序遍历最后一个左子树节点,就能得到左子树长度
- 写完后运行报第一行
#include <iostream>core dump,不知道咋回事,面试官让把cout改为printf试试,还是一样的报错,然后面试官说那就直接看看我的算法逻辑好了。看的时候我讲了以下我的逻辑,面试官说大概逻辑是对的,但可能是忘了判断一些边界条件了。确实是……然后加了两行正确运行了

反问:
- 那边实习生主要作什么?
- 两个方向,冷启动和社交网络,看具体安排
- 转正机制以及转正时间节点
- 9 月、10 月,只要 mentor 对你的表现还比较满意,转正应该就没啥问题
- 面试结果什么时候能通知到呢?
- 要一周左右。。。。。。
