一面:
1、链表反转
2、字符串的最长公共子序列(输出该子序列,用的动态规划)
动态规划的思路:
package DynamicProgramming;
public class LongestCommonSubSequence {
public static int[][] findLongestSubStrDP(String str1,String str2){
char[] char1 = str1.toCharArray();
char[] char2 = str2.toCharArray();
int[][] dp = new int[char1.length][char2.length];
for(int i=0;i<char1.length;i++){
for(int j=0;j<char2.length;j++){
if(i==0){
dp[i][j] = (char2[j] == char1[i] ? 1:0);
}
else if(j==0){
dp[i][j] = (char2[j] == char1[i] ? 1:0);
}
else{
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
if(char1[i] == char2[j])
dp[i][j] = Math.max(dp[i][j],dp[i-1][j-1] + 1);
}
}
}
return dp;
}
public static String lcse(String str1,String str2,int[][] dp){
char[] char1 = str1.toCharArray();
char[] char2 = str2.toCharArray();
int m = char1.length-1;
int n = char2.length-1;
char[] res = new char[dp[m][n]];
int index = res.length-1;
while(index >= 0){
if(n>0 && dp[m][n] == dp[m][n-1])
n--;
else if(m>0 && dp[m-1][n] == dp[m][n])
m--;
else{
res[index--] = char1[m];
m--;
n--;
}
}
return String.valueOf(res);
}
public static void main(String[] args){
String str1 = "1A2C3D4B56";
String str2 = "B1D23CA45B6A";
int[][] dp = findLongestSubStrDP(str1,str2);
String res = lcse(str1,str2,dp);
System.out.println(res);
}
}
3、介绍项目
4、XGBOOst和GBDT的区别。
• 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
• 传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。
• xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性。
• Shrinkage(缩减),相当于学习速率(xgboost中的eta)。xgboost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把eta设置得小一点,然后迭代次数设置得大一点。(补充:传统GBDT的实现也有学习速率)
• 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
• 对缺失值的处理。对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。
• xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的?注意xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
• 可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点。
二面:
1、介绍项目
2、RNN简单介绍一下,BPTT推导
3、后面又写了三层神经网络的推导
4、python中的dict,如何按照值去排序(面试官想考的是lambda函数)
5、python中,如何交换两个数的值,x,y = y,x
6、强化学习和监督学习的区别
1)强化学习是一个多次决策的过程,可以形成一个决策链,即西瓜书上种西瓜的例子;监督学习只是一个一次决策的过程。
2)有监督学习的训练样本是有标签的,强化学习的训练是没有标签的,它是通过环境给出的奖惩来学习。
3)监督学习的学习目标是跟给定的标签越接近越好,而强化学习不是,它希望能够获得的reward越大越好。
7、强化学习DQN有哪些改进方向
Double -DQN/优先经验回放/Dueling-DQN
8、神经网络里面的损失函数有哪些
我写了交叉熵和平方损失,用python实现一个交叉熵函数,考虑的严谨一些
9、机器学习中常见的激活函数有哪些?
10、为什么通常需要零均值
Sigmoid 的输出不是0均值的,这是我们不希望的,因为这会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响:假设后层神经元的输入都为正(e.g. x>0 elementwise in ),那么对w求局部梯度则都为正,这样在反向传播的过程中w要么都往正方向更新,要么都往负方向更新,导致有一种捆绑的效果,使得收敛缓慢。
11、如果逻辑回归的所有样本的都是正样本, 那么它学出来的超平面是怎样的?
所有数据点分布在超平面的一侧
12、你本科学习的方向有点怪(信息管理与信息系统和金融学的双学位),前两份实习也有点瞎找的感觉,你是如何思考的?
13、树的前序遍历和zigzag遍历(非递归)。
zigzag遍历:两个栈
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
if(root == null)
return res;
Stack<TreeNode> level = new Stack<TreeNode>();
level.push(root);
boolean flag = true;
while(!level.isEmpty()){
Stack<TreeNode> tmp = level;
level = new Stack<TreeNode>();
List<Integer> temp = new ArrayList<Integer>();
while(!tmp.isEmpty()){
TreeNode t = tmp.pop();
temp.add(t.val);
if(flag){
if(t.left != null)
level.push(t.left);
if(t.right != null)
level.push(t.right);
}
else{
if(t.right != null)
level.push(t.right);
if(t.left != null)
level.push(t.left);
}
}
flag = !flag;
res.add(temp);
}
return res;
}
}
三面:
1、自我介绍
2、项目探讨
3、自己的职业规划是怎样的
4、你找工作更看重的是哪一个方面
5、对硬性条件的要求
6、业务交流