2021年5月底,好未来-算法实习面试题5道

127 阅读3分钟

这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

5月份好未来还在大力招人,尤其对应届生来说能去这里上班那是相当不错了,七月在线的学员有不少通过了面试。

1.讲一下xgb与lgb的特点与区别

  • xgboost采用的是level-wise的分裂策略,而lightGBM采用了leaf-wise的策略,区别是xgboost对每一层所有节点做无差别分裂,可能有些节点的增益非常小,对结果影响不大,但是xgboost也进行了分裂,带来了不必要的开销。 leaft-wise的做法是在当前所有叶子节点中选择分裂收益最大的节点进行分裂,如此递归进行,很明显leaf-wise这种做法容易过拟合,因为容易陷入比较高的深度中,因此需要对最大深度做限制,从而避免过拟合。
  • lightgbm使用了基于histogram的决策树算法,这一点不同与xgboost中的 exact 算法,histogram算法在内存和计算代价上都有不小优势。
    • 内存:直方图算法的内存消耗为(#data* #features * 1Bytes)(因为对特征分桶后只需保存特征离散化之后的值),而xgboost的exact算法内存消耗为:(2 * #data * #features* 4Bytes),因为xgboost既要保存原始feature的值,也要保存这个值的顺序索引,这些值需要32位的浮点数来保存。
    • 计算:预排序算法在选择好分裂特征计算分裂收益时需要遍历所有样本的特征值,时间为(#data),而直方图算法只需要遍历桶就行了,时间为(#bin)

2.提到了xgb的二阶泰勒展开)讲一下xgb的二阶泰勒展开对于GBDT有什么优势

  • xgboost是在MSE基础上推导出来的,在MSE的情况下,xgboost的目标函数展开就是一阶项+二阶项的形式,而其他类似log loss这样的目标函数不能表示成这种形式。为了后续推导的统一,所以将目标函数进行二阶泰勒展开,就可以直接自定义损失函数了,只要损失函数是二阶可导的即可,增强了模型的扩展性。
  • 二阶信息能够让梯度收敛的更快。一阶信息描述梯度变化方向,二阶信息可以描述梯度变化方向是如何变化的。

3.算法题:字母a-z对应数字1-26,给定一个数字序列, 求所有可能的解码总数,例如 1261 对应解码为 1 2 6 1 , 12 6 1, 1 26 1总共为3种方式

leetcode-cn.com/problems/de…

参考答案:

代码如下:

class Solution:
    def numDecodings(self, s: str) -> int:
        n = len(s)
        s = ' ' + s
        f = [0] * (n + 1)
        f[0] = 1
        for i in range(1,n + 1):
            a = ord(s[i]) - ord('0')
            b = ( ord(s[i - 1]) - ord('0') ) * 10 + ord(s[i]) - ord('0')
            if 1 <= a <= 9:
                f[i] = f[i - 1]
            if 10 <= b <= 26:
                f[i] += f[i - 2]
        return f[n]

4.介绍xgboost和gbdt的区别

  • 传统的GBDT以CART树作为基学习器,XGBoost还支持线性分类器,这个时候XGBoost相当于L1和L2正则化的逻辑斯蒂回归(分类)或者线性回归(回归);
  • 传统的GBDT在优化的时候只用到一阶导数信息,XGBoost则对代价函数进行了二阶泰勒展开,得到一阶和二阶导数;
  • XGBoost在代价函数中加入了正则项,用于控制模型的复杂度。从权衡方差偏差来看,它降低了模型的方差,使学习出来的模型更加简单,放置过拟合,这也是XGBoost优于传统GBDT的一个特性;
  • shrinkage(缩减),相当于学习速率(XGBoost中的eta)。XGBoost在进行完一次迭代时,会将叶子节点的权值乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。(GBDT也有学习速率);
  • 列抽样:XGBoost借鉴了随机森林的做法, 支持列抽样, 不仅防止过 拟合,还能减少计算;
  • 对缺失值的处理: 对于特征的值有缺失的样本,XGBoost还可以自动 学习出它的分裂方向;
  • XGBoost工具支持并行。Boosting不是一种串行的结构吗?怎么并行 的?注意XGBoost的并行不是tree粒度的并行,XGBoost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。XGBoost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),XGBoost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代 中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。

5.算法题:给定一个int数字,将其转换为中文描述,例如,1010 -> 一千零一十

blog.csdn.net/rensihui/ar…

参考代码:

import random
 
# number_to_chinese  讲整数转化为汉字
num_dict = { 0: "零", 1: "一", 2: "二", 3: "三", 4: "四",
             5: "五", 6: "六", 7: "七", 8: "八", 9: "九" }
unit_map = [ ["", "十", "百", "千"],       ["万", "十万", "百万", "千万"],
             ["亿", "十亿", "百亿", "千亿"], ["兆", "十兆", "百兆", "千兆"] ]
unit_step = ["万", "亿", "兆"]
 
class int_number2chinese():


    def __init__(self):
        self.result = ""
 
    def number_to_str_10000(self, data_str):
        # 一万以内的数转成大写
        res = []
        count = 0
        # 倒转
        str_rev = reversed(data_str) 
        for i in str_rev:
            if i is not "0":
                count_cos = count // 4  # 行
                count_col = count % 4   # 列
                res.append(unit_map[count_cos][count_col])
                res.append(num_dict[int(i)])
                count += 1
            else:
                count += 1
                if not res: res.append("零")
                elif res[-1] is not "零":
                    res.append("零")
        res.reverse()
        # 去掉"一十零"这样整数的“零”
        if res[-1] is "零" and len(res) is not 1:
            res.pop()
 
        return "".join(res)
 
    def number_to_str(self, data):
        # 每4位数字进行一次分割
        len_data = len(data)
        count_cos = len_data // 4  # 行
        count_col = len_data-count_cos*4  # 列
        if count_col > 0: count_cos += 1
        res = ""
        for i in range(count_cos):
            if i==0:  data_in = data[-4:]
            elif i==count_cos-1 and count_col>0:
                data_in = data[:count_col]
            else:
                data_in = data[-(i+1)*4:-(i*4)]
            res_ = self.number_to_str_10000(data_in)
            res = res_ + unit_map[i][0] + res
        return res
    def decimal_chinese(self, data):
        assert type(data) == int, "请输入整数"
        data_str = str(data)
        res = self.number_to_str(data_str)
        return res
 
if __name__=="__main__":
    print(n2c.decimal_chinese(23