这篇文章应该是10月份的时候就发出来了,但是DTU太多作业了,做得我人都麻了哎。临近期末了决定铤而走险,一定要把知识点给梳理出来

这篇文章篇幅估计会比较长,因为当时看GBDT的时候发现自己对决策树的一些细节还掌握的不是很到位,所以把 DT -> AdaBoost -> BDT -> GDBT -> LR -> GBDT + LR 整个流程都走了一遍。

有关决策树的概念以及步骤,可以到这翻一翻,下面直接开始讲建树了。但因为重点是是GBDT,所以决策树就讲个大概了

1. 决策树的建树流程

大致的流程:构建根节点,将所有训练数据都放在根节点。

  1. 选择一个最优特征,按照这一特征将训练数据集分割,使得每个每个观测值有一个当前条件下最好的分类。

  2. 如果分对了,就放到对应的叶结点中

  3. 如果分类不正确,那么就对这些子集重新选择新的最优特征,继续分割。

  4. 如此循环下去,直到所有训练数据子集被正确分类,或者没有合适的特征为止。

  5. 以上是生成一棵极有可能过拟合的决策树,于是我们对整棵树进行自下而上地进行剪枝。

其中第四点还需要一些细节补充一下:

  1. 当前结点包含的样本全部属于同一个类别,无需划分;

  2. 当前属性集为空,或是所有样本在所有属性上取值相同,无法划分。在这种情况下,把当前结点标记为叶结点,并且将其类别设定为该结点所含样本最多的类别;

  3. 当前结点包含的样本集和为空,不能划分,把当前结点标记为叶结点,但是将其类别设定为其父结点所含样本最多的类别,

决策树的构建是使得当前的判断规则几乎完美地逼近数据集的分类,而剪枝则是使得决策树具有更强的泛化能力

2. 用什么特征作为区分点呢

  1. ID3:用信息增益去做处理(在给定某一个特征的条件下(就是用这个特征作为区分)整个数据集的熵是多少,然后用区分前喝区分后的熵做一个差)(但是会过度选择特征点比较多的特征,因为可以分得事无巨细,所以信息增益比较大))

  2. C4.5:用信息增益比去做处理(在信息增益的基础上 乘上一个惩罚参数;特征个数较多时(熵小,位于分母所以除以一个小数),惩罚参数较小;特征个数较少时(熵大,位于分母所以除以一个大数),惩罚参数较大)

3. 剪枝

同样可以看开头那篇文章,里面也有写到:剪枝+ID3+C4.5

4. CART树

CART树里面不用ID3,也不用C4.5去做特征选择,而是改用了基尼(GINI)指数去作为衡量标准:

无论是ID3还是C4.5,都是基于熵的模型,里面会涉及到大量的对数运算

        基尼指数的意义是从数据集D中随机抽取两个样本类别标识不一致的概率。基尼指数越小,数据集的纯度越高。

        相比于信息增益,信息增益比等作为特征选择方法,基尼指数省略了对数计算,运算量比较小,也比较容易理解,所以CART树选择使用基尼系数用来做特征选择。

5. CART之回归树      

将输入空间划分为多个区域,每个区域都有一个固定的输出值Cm,这个输出值就是当前这个区域的平均值

然后还是怎么划分的问题。这里采用最小平方误差的准则去找切分变量J 和 切分点S

也就是那三个min的公式了,用这个公式去求得最好的J和S

例子:切分变量J:年龄,切分点S:从8岁到40岁所以切分点就[8, 40]里面找

6. CART之分类树        

分类树我们就换一个切分准则去衡量:引入基尼指数作为衡量标准

若如果样本集合D根据特征A是否取某一可能值a被分割成D1和D2两个部分

基尼指数Gini(D,A)表示经过A=a分割后集合D的不确定性,基尼指数越大,样本集合的不确定性就越大,所以这个特征分割得不咋滴

所以通过基尼系数去筛选,筛选完后就知道用哪个切分量和哪个切分点了,和回归树很像

7. Bagging 和 Boosting

Bagging:原始小样本不能正确反映数据的真实分布,于是每轮的训练集由从初始的训练集中随机取出的n个训练样本组成(取后放回,所以会包含重复的训练样本),用T次随机采样拟合真实分布,得到T个与原始数据集相同大小的子数据集,分别训练得到T个弱分类器Classifier,然后结合为一个强分类器。

最后就是怎么把T个弱分类器一块变成一个强分类器了:Master上学的就是投票法(vote)。对于一个测试样本,通过T个弱分类器得到L个类别信息,这些信息投票产生最后的类别。如T=10,分类结果分别为:[3,3,3,3,5,5,6,4,4,1],那么这个样本就属于3.

Boosting:boosting也是通过重采样得到多个弱分类器,最后得到一个强分类器。区别是boosting是基于权值的弱分类器集成。具体的表现是:初始化时对每一个训练样本赋予相等的权重1/N,然后用网络训练,每次训练后对错误的训练样本赋予较大的权重,也就是让模型在后续的学习中集中对比较难的训练例进行学习。

最后通过什么方式来组合弱分类器?通过加法模型将弱分类器进行线性组合,比如AdaBoost通过加权多数表决的方式,即 增大 错误率小的分类器的权值,同时 减小 错误率较大的分类器的权值。

        两者的区别:

1. 样本选择上:

Bagging:训练集是在原始集中有放回选取的,从原始集中选出的各轮训练集之间是独立的。

Boosting:每一轮的训练集不变,只是训练集中每个样例在分类器中的权重发生变化。而权值是根据上一轮的分类结果进行调整。

2. 样例权重:

Bagging:使用均匀取样,每个样例的权重相等

Boosting:根据错误率不断调整样例的权值,错误率越大则权重越大。

3. 最终分类器的权重表达:

Bagging:所有预测函数的权重相等

Boosting:每个弱分类器都有相应的权重,对于分类误差小的分类器会有更大的权重。

4. 并行计算:

Bagging:各个预测函数可以并行生成

Boosting:各个预测函数只能顺序生成,因为后一个模型参数需要前一轮模型的结果。

一些耳熟能详的模型是怎么来的:

Bagging + 决策树 = 随机森林

        AdaBoost + 决策树 = 提升树

        Gradient Boosting + 决策树 = GBDT

其中第二第三种我们在后面会说到!

8. 随机森林

每棵决策树都是一个分类器,那么对于一个输入样本,N棵树会有N个分类结果。而随机森林集成了所有的分类投票结果,将投票次数最多的类别指定为最终的输出,这就是一种最简单的 Bagging 思想。

9. AdaBoost

        通过不断把一些小的model组合起来,形成一个大的model,然后小的model之间还会有联系

首先,我们先初始化每一个样本的权重,刚开始我也不知道哪个好,所以大家权重都是 

然后训练当代的分类器 

然后,计算当前这个分类器的训练误差:

这个是代表当前这个分类器重不重要(权重), 越大,这个  就越小

然后更新权值,这个权值是要给下一个分类器使用的:其实就是根据训练误差,来重新安排训练样本的权值,Z_m就是相当于softmax的分母,归一化到0-1的概率中了

最后,当我们的分类满足条件,就可以把每一个基分类器  给整合起来:

9. 前向分步算法

上面提到了的AdaBoost模型表示

下面先来了解一下加法模型的一般表示方法:

我们的目标是训练出一个好的模型,也就是loss比较低的模型,所以我们用loss指标去衡量这个模型好不好:

我们可以看到学习加法模型里面,有可能会有很多参数在里面,如果一起学习,那么变量太多就很难快速地找到最优解,于是我们打算每一步只学习一个基函数及其系数,然后逐步逼近优化目标上式损失函数极小化函数

所以流程就是先把:

给学出来,然后再学:

然后更新我们的加法模型:

10. AdaBoost 和 前向分步算法的结合

        AdaBoost的损失函数就是指数损失函数,具体的表现形式:

然后,把AdaBoost的公式带入,并改写:

可以看出上面的公式就是把Adaboost的表达式代替了加法模型的一般式子,然后再套进AdaBoost的损失函数里面。

继续改写:

其中:

改写后的式子就是我不关心了,现在就只管 , 我们把它俩求出他们来,达到当前最小值

要想达到整体最小值,我们先让每一部分都是最小值

首先就是就是最小值 G,因为我们这里是用 log损失函数 来衡量,所以G其实是以下的写法:

也就是分错类的value*weight(因为是loss嘛),所以我们要argmin这个G才可以

现在我们求出了当前代的G,然后求这个分类器对应的权值(就是求,来使得整体最小)

代入上面求得的:

我们用求导的方式去求极值,就得到当前这一代的最小值了

11. 提升树(Boosting Decision Tree)

其实我们是想用AdaBoost来引出BDT,然后引出GBDT

同理,我们先把BDT给表示出来,其实BDT是决策树的加法模型!

        但是提升树里面每一棵树都是没有权值的,因为

回归提升树是一个拟合残差的过程。与Adaboost不同的是:adaboost使用多个基学习器来拟合训练集数据(可以看成都具有相同的目标函数),而基学习器训练的结果有好有坏。基于训练结果好的给一个较大比重的设定,来分析各个基学习器的比重。而回归提升树是一个不断减少残差的过程,这是与adaboost最大的不同。按照adaboost权重设定思路,训练结果好的比重大,那么在boosting算法中,第一棵树一定是比重最大的。

并且提升树也是用前向分步算法的。所以我们可以改写成前向分步的表达方式:

然后也是用loss去衡量:

然后提升树这里我们选择是一个回归的提升树,用平方误差作为损失函数:

: 这个r就是表示残差。上一个模型做错的地方就是残差,然后我们的T就是去拟合这个残差

拟合这个残差的意思是,把残差作为输出值放到下一个模型(基分类器)的输入里面,进行回炉重造。如果我学到了,我通过加法模型就可以不断地缩小找个残差,从而达到拟合的目的

        所以,整个提升树流程就是:(网上找的图!)

因为咱们选的是回归树,其实是一课CART回归书,所以我们做特征选择的时候就用三个min就好了。

12. 梯度提升之GBDT(Gradient Boosting Decision Tree)

提升树利用加法模型和前向分布算法实现学习的优化过程, 但损失函数是平方损失或者指数损失时, 优化比较简单(就是比较计算的意思)

但是对于一般的损失函数(有一些损失函数是手算算死你啊)而言, 往往每一步优化(计算)不容易, 针对这个问题, 所以Friedman大神提出了利用最速下降的近似方法, 即利用损失函数的负梯度来拟合基学习器。

为什么是负梯度呢?(摘抄自知乎)

  1. 负梯度永远是函数下降最快的方向,自然也是gbdt目标函数下降最快的方向,所以用梯度去拟合首先是没什么问题的(并不是拟合梯度,只是用梯度去拟合,发现好多人搞错);gbdt本来中的g代表gradient,本来就是用梯度拟合;

  2. 用残差去拟合,只是目标函数是均方误差的一种特殊情况,这个特殊情况跟CART拟合残差一模一样,使得看起来就拟合残差合情合理。

  3. 为啥要去用梯度拟合不用残差?代价函数除了loss还有正则项,正则中有参数和变量,很多情况下只拟合残差loss变小但是正则变大,代价函数不一定就小,这时候就要用梯度啦,梯度的本质也是一种方向导数,综合了各个方向(参数)的变化,选择了一个总是最优(下降最快)的方向;

所以是一个重点:我们现在是用梯度去拟合残差 

怎么拟合残差呢?首先看残差怎么来的:

,所以是”看当前这个模型做错了什么“,也就是做的不够好的地方,也就是我们下一个模型即将要去做好的地方,也就是下一个模型和当前这个模型的diff,所以就是变成对这个模型求梯度了。

目标函数还是我们的loss:对模型求导:

也就是说:

所以GBDT的流程是:(图也是别人的)

再用大白话说一下:首先就有一棵树,然后做LOSS,根据LOSS对树求导,得到梯度 (1),找个梯度就是残差,也就是下一颗树要提升的地方。然后我们用这个残差去得到下一颗树 (2),这棵树我们用wt来表示,然后这棵树就是要push进模型里面的,但是我们还要去计算一下这棵树在总树里面的权重 (3),得到权重之后,就可以push进总树了(4)。

但是这里有一个问题,为什么提升树是没有权重的,但是反而梯度提升树是有权重的呢?这里的权重指的是树与树之间,而不是样本与样本之间的权重

下面用一个例子去说明GBDT的流程,这里的例子有助于后面理解GBDT的输出是怎么变成LR的输入!参考的是这位大佬的例子

树的深度max_depth=3,迭代次数:n_trees=5

这里预测的,其实不是预测身高是多少,当然这里可以这么认为,但是我们是要回归到二分类的目的上,我们可以把最后一列的属性换成“是否点击“,这样0就是没点,1就是点,这样就可以通过年龄和体重去预测4号样本有没有点击了。这才是问题的根本

1.初始化第一个弱学习器

我们看上面的步骤,先最小化损失函数

CART树用的是平方损失函数

然后求最小值,直接求导

所以 (1.1 - h) + (1.3 - h)+ (1.7 - h)+(1.8 - h) = 0,求得 h = 1.475

所以第一个弱分类器

2. 进入迭代周期

就是for 循环,迭代次数自己控制(我们选择了5)

然后计算负梯度拟合残差

我们再来回忆一下刚才我们计算出来的残差到底是麻子玩意儿:-(-y_i + p_i) = y_i-p_i

我们现在就带入嘛,根据我们要预测的“身高”,得到第一批残差:

编号:0 -> 1.1 - 1.475 = -0.375

编号:1 -> 1.3 - 1.475 = -0.175

编号:2 -> 1.7 - 1.475 = 0.225

编号:3 -> 1.8 - 1.475 = 0.325

这就很关键了,我们拿着四个残差去作为下一个弱训练器的训练数据

然后就是根据这个残差,我们根据CART回归树怎么找特征点,特征值去作为最优解来划分数据:就是那3个min的平方损失公式,启发式学习一个一个地去找,

现在我们假设年龄的7岁作为特征点,以7作为分界线就是特征值去划分数据,小于等于7的都是左边,大于7的都是右边

于是我们得到左节点包括 0, 右边的节点:1,2,3,其中左边的loss=0,右边的loss=0.14 总的loss=0.14

这个大佬还给出了所有的计算结果,牛啊 牛啊

以上划分点是的总平方损失最小为0.025有两个划分点:年龄21和体重60,所以随机选一个作为划分点,这里我们选 年龄21

  现在我们的第一棵树长这个样子

我们设置的参数中树的深度max_depth=3,现在树的深度只有2,需要再进行一次划分,这次划分要对左右两个节点分别进行划分:

继续用CART回归树找特征的方法,3个min的平方损失函数去做处理

至此:

我们拿到了第一课决策树了

此时我们的树深度满足了设置,还需要做一件事情,给这每个叶子节点分别赋一个参数,来拟合残差。 就是第一步做的:

然后我们要计算每一个叶子附一个参数,来拟合残差:j代表第几个叶子,m是第几棵树, 那个下标代表如果当前叶子有多个样本在,我们把多个样本都拿进来一块算这个

这里其实和上面初始化学习器是一个道理,平方损失,求导,令导数等于零,化简之后得到每个叶子节点的参数,其实就是标签值的均值。这个地方的标签值不是原始的y,而是本轮要拟合的标残差 y-f_0(x) ​ 也就是第一个叶子的value输出是-0.375,当我们把四个样本的values都算出来,他们会进入下一棵回归树(真的是新的,不是在这个基础上添加一棵子树的意思),像刚进来的时候这么玩,就像下图:

下面是计算我们用x棵树去做拟合,有多接近呢?

我们先看0这个样本:其实就是拿我们上面得到的GBDT损失函数直接写,这里把分类的预测概率转换成了对数几率回归的形式,就是我们熟悉的,F的下标代表是第0(1:反正就是第一棵)棵树:

但是啊,这个式子要求出我觉得也挺难的,强哥这里给出很牛逼的思路,我们用泰勒公式去逼近上面的损失函数,我们把当作泰勒展开里面的,然后 当作常量 ,然后二阶展开:

两边都对求导,然后有:

然后左边等于0,因为求最小值嘛,倒数肯定等于0

然后右边坐一下变换,可以得到:

这里解释一下,的下标10就是第一个叶子,然后用第0棵树

然后损失函数的一阶导就是我们之前算过了:就是第一棵树的第一个样本的残差

然后二阶导,上图吧,写不动了:

这里的p1,其实就是第一棵树我们用来做split的value:h = 1.475

至此,可以解出第一片叶子的了,然后我们更新第一片叶子里面所有样本的残差

手动算第一片叶子试一下:

然后第一个样本的进去后面一棵树的残差就是 -0.375 + 0.535 = 0.26,你看是不是离1.1 越来越近了哈哈哈

这其实是在算 我们用第一棵树去做预测,会有多接近真实值。后面还有4棵呢

13. GBDT+LR

        这一节基本是跟着强哥的博客在学习(没有抄袭的意思!只是想再整理成自己的思路))

        提督提升决策树和逻辑回归的融合!

        比于协同过滤和矩阵分解利用用户的物品“相似度”进行推荐, 逻辑回归模型将问题看成了一个分类问题, 通过预测正样本的概率对物品进行排序。

这里的正样本可以是用户“点击”了某个商品或者“观看”了某个视频, 均是推荐系统希望用户产生“正反馈”行为, 因此逻辑回归模型将推荐问题转成成了一个点击率预估问题。

要注意这和前面的协同过滤不太一样了, 那里是“TOPN"推荐的问题, 而这里通过逻辑回归转成了一种点击率预估问题, 成了一种二分类, 如果模型预测用户会点击, 那么就进行推荐。

逻辑回归(LR)的知识点可以到这参考吗,之前总结过了

GBDT+lR 是利用GBDT自动进行特征筛选和组合, 进而生成新的离散特征向量, 再把该特征向量当做LR模型的输入, 来产生最后的预测结果, 这就是著名的GBDT+LR模型了。

        训练时

GBDT 建树的过程相当于自动进行的特征组合和离散化。

x为一条输入样本,遍历两棵树后,x样本分别落到两颗树的叶子节点上,每个叶子节点对应LR一维特征,那么通过遍历树,就得到了该样本对应的所有LR特征。

        预测时

会先走 GBDT 的每棵树,得到某个叶子节点对应的一个离散特征(即一组特征组合),然后把该特征以 onehot 形式传入 LR 进行线性加权预测。

        其中,要注意的是,GBDT的结果是怎么转换成LR的输入的:

用已有特征训练GBDT模型,然后利用GBDT模型学习到的树来构造新特征,最后把这些新特征加入原有特征一起训练模型。构造的新特征向量是取值0/1的,向量的每个元素对应于GBDT模型中树的叶子结点。当一个样本点通过某棵树最终落在这棵树的一个叶子结点上,那么在新特征向量中这个叶子结点对应的元素值为1,而这棵树的其他叶子结点对应的元素值为0。新特征向量的长度等于GBDT模型里所有树包含的叶子结点数之和。

怎么理解这个落在:一开始我想了非常久,还把知识点从头看一遍。。。发现自己真的是越看越出不来,这整棵树不是在做一个分类嘛相当于,到最后的所有叶子结点肯定会包含所有样本,有的叶子结点不只是一个样本而已。然后落在那个叶子结点,这个叶子结点对应的向量的value就是1

例子:总共两棵树: 左树有三个叶子节点,右树有两个叶子节点,最终的特征即为五维的向量。对于输入x,假设他落在左树第二个节点,编码[0,1,0],落在右树第二个节点则编码[0,1],所以整体的编码为[0,1,0,0,1],这类编码作为特征,输入到线性分类模型(LR or FM)中进行分类。

        GBDT为什么可以实现建树的过程相当于自动进行的特征组合和离散化

因为从根结点到叶子节点的这条路径就可以看成是不同特征进行的特征组合,用叶子节点可以唯一的表示这条路径,并作为一个离散特征传入 LR 进行二次训练。

        树模型不能处理大量高维度离散数据的原因是容易导致过拟合, 但是具体是怎么导致的过拟合呢? 因为现在的模型普遍都会带着正则项,而LR等线性模型的正则项是对权重的惩罚,也就是w一旦过大,惩罚就会到了,然后立马压缩w的值从而来减少过拟合。而树模型则不一样,树模型的惩罚项通常为叶子结点数和深度,我们都知道,高维且离散的特征来说,树只要一个节点就可以完美地把离散特征给区分开来,所以它对应的惩罚项极其小,这也就是为什么在高维稀疏特征的时候,线性模型会比非线性模型好的原因了,因为树模型已经对那一部分高维且离散的特征过拟合了,但是树模型的惩罚项还惩罚不到这一点,所以带正则化的线性模型比较不容易对稀疏特征过拟合(这个回答在知乎上搬运的)

通过GBDT进行特征组合之后得到的离散向量是和训练数据的原特征一块作为逻辑回归的输入, 而不仅仅全是这种离散特征,其中 GBDT一般会建立两类树(非ID特征建一类, ID类特征建一类), AD,ID类特征在CTR预估中是非常重要的特征,直接将AD,ID作为feature进行建树不可行,故考虑为每个AD,ID建GBDT树。GBDT+LR模型, GBDT的输入特征一般不能是高维稀疏的id离散特征, 树模型是不喜欢这种特征的。 所以如果有大量的id类特征, 一定要放到LR这块里面,也就是让连续的特征过gbdt, 然后得到输出的新特征和离散特征走LR。

推荐系统之 GBDT和GBDT+LR相关推荐

  1. 推荐系统 | 基础推荐模型 | GBDT+LR模型 | Python实现

    基础推荐模型--传送门: 推荐系统 | 基础推荐模型 | 协同过滤 | UserCF与ItemCF的Python实现及优化 推荐系统 | 基础推荐模型 | 矩阵分解模型 | 隐语义模型 | PyTor ...

  2. 推荐系统(二)GBDT+LR模型

    推荐系统(二)GBDT+LR模型 推荐系统系列博客: 推荐系统(一)推荐系统整体概览 在写这篇博客之前,一度纠结许久,到底该不该起这个标题,因为把GBDT+LR模型放在推荐系统系列里,似乎有些不妥,如 ...

  3. 推荐系统-排序算法:GBDT+LR

    1. GBDT + LR 是什么 本质上GBDT+LR是一种具有stacking思想的二分类器模型,所以可以用来解决二分类问题.这个方法出自于Facebook 2014年的论文 Practical L ...

  4. 推荐系统(基于CB,CF,LR)

    数据预处理 用户画像数据user_profile.data(10w条,5.79M): userid, 性别, 年龄段, 收入段, 地域 物品元数据music_meta(75w条,117M): item ...

  5. 推荐系统fmlr_推荐系统实践 0x0c FM系列(LR/FM/FFM)

    逻辑回归(LR) 在介绍FM系列之前,我想首先简单介绍一下逻辑回归.通常来说,逻辑回归模型能够综合利用更多的信息,如用户.物品.上下文等多种不同的特征,生成更为全面的结果.另外,逻辑回归将推荐问题看成 ...

  6. 《推荐系统笔记(十)》CTR预估以及一般算法介绍(GBDT+LR)

    前言 CTR预估是推荐系统中重要的问题,根据历史数据,我们需要预测用户是否点击.CTR预估同样可以运用到广告中,预测广告的点击率等. CTR数据形式 CTR预估问题的数据形式一般是这样的: 列名:特征 ...

  7. 推荐系统组队学习——GBDT+LR

    文章目录 一.逻辑回归模型 二.GBDT模型 三.GBDT+LR模型 四.编程实践 一.逻辑回归模型 逻辑回归是在线性回归的基础上加了一个 Sigmoid 函数(非线形)映射,使得逻辑回归成为了一个优 ...

  8. 推荐系统入门(五):GBDT+LR(附代码)

    推荐系统入门(五):GBDT+LR(附代码) 目录 推荐系统入门(五):GBDT+LR(附代码) 引言 1. GBDT模型 2. LR模型 3. GBDT+LR模型 4. 编程实践 实战 思考 参考资 ...

  9. 推荐系统——GBDT+LR

    [[逻辑回归模型]] 逻辑回归是在[[线性回归]]的基础上添加了一个Sigmoid函数(非线形)映射,从而可以使逻辑回归成为一个优秀的分类算法 逻辑回归假设数据服从[[伯努利分布]],通过[[极大化似 ...

最新文章

  1. Java 中文转拼音
  2. 进阶学习(3.2)Factory Method Pattern 工厂方法模式
  3. 前端学习(2466):在前端页面中引入百度地图
  4. concurrenthashmap实现原理_Mybatis:PageHelper分页插件源码及原理剖析
  5. linux dhcp客户端配置文件,各个版本DHCP配置文件的整理
  6. 简书自动生成目录小工具
  7. [置顶] 让我爱恨的ThinkPHP Relation
  8. 如何在终端窗口中在Linux中创建文件?
  9. Delphi版 ArcEngine Mapcontrol与PageControl同步
  10. 排序算法----------堆排序
  11. isp邮件服务器是什么,与ISP企业邮箱共建邮件服务器
  12. 强训之【走方格的方案数和另类加法】
  13. Vue项目中的自定义指令
  14. 有15个数按由大到小顺序存放在一个数组中,输入一个数,要求用折半查找法找出该数是数组中第几个元素的值。如果该数不在数组中,则输出“无此数”
  15. 让数据填报、收集效率提升80%!这个报表工具真的太强大了
  16. win10桌面图标有个白板,怎么去掉
  17. 计算机软件发展的指标,信息化发展指数
  18. 五步详解小学数学之盈亏问题
  19. 【设计模式】抽象工厂模式 Abstract Factory Pattern
  20. 深富策略是正规合法平台:公私募称市场不具系统性风险

热门文章

  1. python世界你好的输出便携电源适配器_65W PD 输出,thinkplus USB-C 便携电源适配器(PA65)开箱评测...
  2. 最新自动搞蚂蚁森林任务工具
  3. 【二叉搜索树】c++实现二叉搜索树
  4. 关于%d%s的详细使用方法
  5. 基于JAVA房屋租赁网站计算机毕业设计源码+系统+lw文档+部署
  6. 计算机雕刻出什么东西,说说玉器雕刻之手工和电脑工
  7. 贝叶斯、先验概率后验概率
  8. CSS3实现王者荣耀匹配人员加载页面
  9. JavaWeb是什么?总结一下JavaWeb的体系
  10. cxfreeze打包python2.7为exe可执行程序