【量化课堂】Boosting 介绍和 Python 实现【记录我的学习】

一面兴奋进来、看到公式到结束一面懵逼出去、还没理解完全部,加油咯。。。

引言:Boosting 是一种集成算法,经常使用 决策树(decision tree) 作为基础分类器,有些 Boosting 模型也用逻辑回归(logistic regression),SVM 等方法做分类器的,倘若读者初学机器学习,学习这部分时建议补完决策树的相关知识,帮助理解。本文主要用 Boosting 的始祖算法 AdaBoost 为例介绍其实现流程,希望了解更复杂其他 Boosting 算法的可以看本系列其他文章。

本文由 JoinQuant 量化课堂推出 。难度标签为入门,需要至少了解一种机器学习模型(决策树,逻辑回归,SVM 等),理解深度标签:level-1

作者: 大白
编辑: 肖睿

 

简介:

机器学习中单一的分类器能力有限,也往往达不到充分利用计算机算力,所以多个弱学习集组合来提升总的分类效果就是改进方向之一,Boosting 就是这类集成算法中的典范。

本章分为:
1, Boosting 的由来
2, Boosting 和 AdaBoost 的思想及流程

  • 1) 思想
    2) 算法过程
    3) AdaBoosting 的产生和数学推导

3,Boosing 和 AdaBoost 主要优点及数学证明:
4,AdaBoost 的 Python 应用举例
5,补充和扩展

1,Boosting 的由来

Boosting 是典型的集成算法,它通过结合多个弱分类器(如决策树,逻辑回归),从而形成一个强分类器。但不同于 Bagging 或者 Random Forest 等,Boosting 每个树的生成都基于上一个分类器的结果,会有针对性的将上一个分类器分错的样本提高权重,再建立新的数。这样新的综合的分类器会不断学习一次次分类错误的结果,从而提升算法的能力已达到强分类器的效果。Boosting 算法的始祖是 AdaBoost, 由于其具有 1,在训练集上的错误率上界必然会逐渐下降;2,即使训练次数很多,过拟合的情况也不明显。这些重要优点使它在机器学习中脱颖而出。

在 1990 年,Robert Schapire 构造出一种多项式级算法,可以将偌分类器组合成强分类器,及 Boosting 算法。次年,Yoav Freund 提出了一种效率更高的 Boosting 算法,但这两种最初的算法都有缺陷:都需要知道弱学习算法学习正确率的下限。1995 年,Freund 和 Schapire 改进了 Boosting 算法,提出 AdaBoost 算法。这一算法在效率与之前算法相同的情况下却不需要任何关于弱学习器的先验知识,适用范围大大增加。之后之后,两位创始人进一步提出 AdaBoost.M1(从二分类扩展到多分类问题)、AdaBoost.M1(引入置信度的概念)等算法,在机器学习领域影响深远。Boosting 在人脸识别,文本分类,推荐算法等领域应用广泛。

 

2,Boosting 的思想及流程

2.1 思想:

Bossting 的主要思想非常简单。首先先建立 MMM 个模型(例如 MMM 个决策树分类模型),这 MMM 个模型一般都是比较简单的,被称为弱分类器(weaker learner),Boosting 模型会在每次分类后都把错误数据的权重提高,再用下一个合适的弱分类器进行分类,最后综合全部结果得到最终结果。
aa3c5328d1cd4c2688eaebb342bd28f4.png
Boosting 模仿了人类的根据错误来学习的学习流程:首先用一种方法尝试,得到结果;集中注意力与上次做错的的地方,在方法库里找一种处理旧的错误更好的方法,并用第二种方法补充第一种方法得到新的可以规避错误的方法…… 最后在反复衡量各个方法的错误后,归纳成一个对错误数据比较敏感,处理能力更好地综合算法。

2.2 算法过程:

在这我们使用 Boosting 中代表性的算法 AdaBoost 来演示 Boosting 的运行详细流程和数学参数更新。AdaBoost 是 Boosting 的鼻祖,AdaBoost 会先选择其中最好的模型进行第一次分类,得到分类结果;第二次循环再提升第一次分错的数据重要性占比,重新更新比重后选择新的最优分类器,权衡前面全部分类器(第二部时有两个)的结果得到新的最终分类器;第三次……, 如此循环,直到约束条件满足。
281852133944754.jpg

算法过程如下:
1, 首先,给定一些点 (x1,y1),(x2,y2),…,(xm,ym) xi∈X(x1,y1),(x2,y2),…,(xm,ym) xi∈X(x1,y1),(x2,y2),…,(xm,ym) xi∈X,是特征空间的坐标(可能如图 A 所示是二维坐标,也可能是更高维度,便于理解读者把它当做二维空间想象即可);yi∈{1,−1}yi∈{1,−1}yi∈{1,−1}, 是数据属性的标签。
并初始化每个点的比重 D1(i)=1mD1(i)=1mD1(i)=1m

2,迭代开始,对于 t=1,2,…,Tt=1,2,…,Tt=1,2,…,T, 每次迭代的步骤为:

2-1 寻找最优的弱分类器:
hththt 是第 ttt 次迭代最优的分类器,εεε 是分类器的误差,yiyiyi 是真实值,H(xi)H(xi)H(xi) 是预测值

 

ht=argminεj=argmin∑i=1mDt(i)|{yi≠hj(xi)}|

ht=argminεj=argmin∑i=1mDt(i)|{yi≠hj(xi)}|ht=argminεj=argmin∑i=1mDt(i)|{yi≠hj(xi)}|

 

2- 2 当误差 εt≥12εt≥12εt≥12 时,已近找不到可以提升算法的分类器,结束迭代
2-3 获取分类器权重 αtαtαt (推导部分会详述如何得到这一值)

 

αt=12log1−εε

αt=12log1−εεαt=12log⁡1−εε

 

2-4 更新每个点的权重,返回 2 开始,进行新一轮迭代

 

Dt+1(i)=Dt(i)exp(−αtyiht(xi))Zt

Dt+1(i)=Dt(i)exp(−αtyiht(xi))ZtDt+1(i)=Dt(i)exp⁡(−αtyiht(xi))Zt

 

其中 ZtZtZt 是一个正则化因子,取

 

Zt=∑i=1mDt(i)exp(−αtyiht(xi))

Zt=∑i=1mDt(i)exp(−αtyiht(xi))Zt=∑i=1mDt(i)exp⁡(−αtyiht(xi))

 

3, 最后的输出结果为:

 

Hx=sign(∑t=1Tαtht(x))

Hx=sign(∑t=1Tαtht(x))Hx=sign(∑t=1Tαtht(x))

 

2.3AdaBoosting 的产生和数学推导:(数学原理部分,可跳过)

AdaBoosting 的流程虽然简单,但希望深入了解或改进的人肯定会问:找到第一个最优的分类器后,错误的点以多大比率提升重要程度?为什么?分类器又如何投票决出最终结果?步骤中的分类器权重 αt=12log1−εεαt=12log1−εεαt=12log⁡1−εε 是如何得到的?每个点的权重又如何更新?

上述全部问题其实就是最后两个问题的体现,在这笔者将 AdaBoost 生成的思路和数学推导整理并做详细介绍,提供给希望了解 AdaBoost 原理甚至做出改进的读者。

从基础来讲 Boosting 方法就是要先建立简单的弱学习器,然后一步步将其改进为强学习器,AdaBoost 也一样,以决策树做基本弱学习器为例,我们从建立第一个弱学习器开始学习 AdaBoost 的原理。

1, 首先,给定一些点 (x1,y1),(x2,y2),…,(xm,ym)(x1,y1),(x2,y2),…,(xm,ym)(x1,y1),(x2,y2),…,(xm,ym); xi∈Xxi∈Xxi∈X,是特征空间的坐标(可能如图 A 所示是二维坐标,也可能是更高维度,便于理解读者把它当做二维空间想象即可);yi∈{1,−1}yi∈{1,−1}yi∈{1,−1}, 是数据属性的标签。
并初始化每个点的比重 D1(i)=1mD1(i)=1mD1(i)=1m
寻找最优的弱分类器 ht=argminεj=argmin∑mi=1Dt(i){yi≠hj(xi)}ht=argminεj=argmin∑mi=1Dt(i){yi≠hj(xi)}ht=argminεj=argmin∑i=1mDt(i){yi≠hj(xi)}

boosting1.PNGfinal1.PNG
这部分比较简单,如上面两个例子所示,第一个最优的决策树被建立了起来,它大致将数据中红蓝点区分了出来。(两幅图都是 AdaBoost 第一颗决策树的划分,第二个图是本文 Python 实验里的图,及 sklearn 的示例,由于网络上第一个版本讲解的比较详细,为了更贴合部分读者的理解,笔者接下来就用第系列图来描述 AdaBoost 流程了)

接下来我们要考虑如何提高分错点的权重,如下图有五个分错点。Adaboost 的思想是提高分错点的权重,降低以及正确点的权重,由于我们目前不知道权重如何分配比较好,只知道这应该与分类器的误差相关,所以暂且假设:倘若分类错误,权重乘 eβeβeβ;倘若分类正确,权重乘 e−βe−βe−β。如下图所示
boosting2.PNGboosting3.PNG

假设预测结果为 h1(xi)h1(xi)h1(xi), 真实结果为 yiyiyi, 第 ttt 轮迭代每个点的权重分别是 Dt(i)Dt(i)Dt(i) 则:

 

Dt+1(i)=Dt(i)exp(−βtyiht(xi))∑mi=1Dt(i)exp(−βtyiht(xi))

Dt+1(i)=Dt(i)exp(−βtyiht(xi))∑mi=1Dt(i)exp(−βtyiht(xi))Dt+1(i)=Dt(i)exp⁡(−βtyiht(xi))∑i=1mDt(i)exp⁡(−βtyiht(xi))

 

我们将分母以 ZtZtZt 表示

 

Zt=∑i=1mDt(i)exp(−βtyiht(xi))

Zt=∑i=1mDt(i)exp(−βtyiht(xi))Zt=∑i=1mDt(i)exp⁡(−βtyiht(xi))

 

其中,当预测错误 yiht(xi)=−1yiht(xi)=−1yiht(xi)=−1 时,错误的点扩大到 eβeβeβ 倍,正确时,yiht(xi)=1yiht(xi)=1yiht(xi)=1,权重缩小比例为 e−βe−βe−β

我们还可以将 Dt+1(i)Dt+1(i)Dt+1(i) 写成与 Dt(i)Dt(i)Dt(i) 无关的形式:
\begin {align*}
D{t+1}(i) &=\frac {D{t}(i)\exp (-\beta _t y_i h_t (x_i))}{Z_t}\
&=\frac {\exp (- \sum
{t} \beta _t y_i h_t (x_i))}{m\prod_t Z_t}
\end {align*}

得到新的每个点权重比例后,即可进行下一轮循环,再根据新的权重寻找最优分类器。如下图所示:

boosting4.PNG

......

......

boosting5.PNG

最后,我们综合上述全部分类器,得到综合分类器为

 

H(x)=sign(α1h1+α2h2+α3h3+…)

H(x)=sign(α1h1+α2h2+α3h3+…)H(x)=sign(α1h1+α2h2+α3h3+…)

 

其中,sign 函数指的是倘若加权结果大于 000,则 H(x)H(x)H(x) 为 111;倘若加权结果小于 000,则 H(x)H(x)H(x) 为 000。ααα 代表每个分类器的投票权重。

Hx=sign(∑t=1Tαtht(x))

Hx=sign(∑t=1Tαtht(x))Hx=sign(∑t=1Tαtht(x))

 

咚咚咚(敲桌子)接下来重点来了,打瞌睡的别睡了,赶紧来听,铺垫结束,接下来是 AdaBoost 的核心思路了。

ααα 分别是每个分类器的投票权重,显然这个权重应该由分类器的误差决定,误差越小则理应投票权重越大。联系之前我们设的另一个与误差相关的变量 ααα,倘若我们在此再设一个变量 βββ,那我们就有两个需要优化的变量 ααα 和 βββ,以及一个自变量误差?。理论上笔者现在应该列一个误差?与变量 ααα 和 βββ 的公式,然后求极值求偏倒得到?最小情况下变量 ααα 和 βββ 与?的公式,从而完成最优解带回最初的流程,完成 AdaBoost 流程。

但事实情况是由于?是离散的,其公式为:

ε=1m|{i:H(xi)≠yi}|

ε=1m|{i:H(xi)≠yi}|ε=1m|{i:H(xi)≠yi}|

 

这个东西根本无法求导,所以我们必须将其转化为一个可求导的函数。由于综合函数 Hx=sign(∑Tt=1αtht(x))Hx=sign(∑Tt=1αtht(x))Hx=sign(∑t=1Tαtht(x))与真实值 yiyiyi 的 误差在 −yif(xi−yif(xi−yif(xi 小于零时为 111,大于 000 时为 000,如下图蓝线所示。对于这样的离散函数,需要找一个连续的大于它的函数来表示其上界,这样转换中虽然有一定损失,但离散的问题就可以转化为可求导的连续性问题了,我们熟悉的函数中自然数的 nnn 此方就是这样的函数,我们可以用 exp(−yif(xi))exp(−yif(xi))exp⁡(−yif(xi)) 来代表其上界,如图,
桌面.PNG

 

[[H(xi)≠yi]]≤exp(−yif(xi))

[[H(xi)≠yi]]≤exp(−yif(xi))[[H(xi)≠yi]]≤exp⁡(−yif(xi))

 

显然上式成立,上式描述了每一个离散的结论在 −yif(xi)≥0−yif(xi)≥0−yif(xi)≥0 为 000; 反之为 111,都在上界 exp(−yif(xi))exp(−yif(xi))exp(−yif(xi)) 之下,那么总的误差的不等式为

 

1m∑i[[H(xi)≠yi]]≤1m∑iexp(−yif(xi))

1m∑i[[H(xi)≠yi]]≤1m∑iexp(−yif(xi))1m∑i[[H(xi)≠yi]]≤1m∑iexp(−yif(xi))

 

推到这一步看到右边的东西是不是感到很熟悉?回想一下我们的点的权重 DDD
\begin {align}
D{t+1}(i) &=\frac {D{t}(i)\exp (-\beta t y_i h_t (x_i))}{Z_t}\
&=\frac {\exp (- \sum
{t} \beta t y_i h_t (x_i))}{m\prod{t} Z_t}
\end {align
}

倘若把错误点权重 eβeβeβ 中的 βββ 等于分类器权重 ααα,则我们转化的误差上限可以等价于 DT+1(i)DT+1(i)DT+1(i) 转换后的分母。则假设 β=αβ=αβ=α

\begin {align}
D{t+1}(i) &=\frac {\text {D}{t}(i) exp (-\alpha t y_i h_t (x_i))}{Z_t}\
&=\frac {exp (- \sum
{t} \alpha _t y_i h_t (x_i))}{m\Pi_t Z_t}\
&=\frac {exp (y_i f (x_i))}{m\Pi_t Z_t}
\end {align
}

根据上式,我们可以得到:

\begin {align}
[![ H (xi) \neq yi]!] &\leq exp (-y_i f (x_i)) \
\frac {1}{m} \sum
{i} [![ H (xi) \neq yi]!] &\leq \frac {1}{m} \sum{i} exp (-yi f (x_i)) \
&=\sum
{i} (\prod Zt) D{T+1}(i) \
&=\prod Z_t
\end {align
}

于是得到了误差的上界 ∏Zt∏Zt∏Zt 那么我们的思路就可以转化为最小化误差上界 ∏Zt∏Zt∏Zt 的问题,只要保证每一步得到的 ZtZtZt 都尽量最小,则误差上界必然减小。
则下一步对 Zt 求导,得

\begin {align}
\frac {dZ}{d \alpha}=-\sum{i=1}^m D (i) y_ih (x_i) e^{y_i \alpha_i h (x_i)} &=0\
\sum
{i: h (xi) = yi} D (i) e^{-\alpha} + \sum{i: h (xi) \neq y_i} D (i) e^{\alpha} &=0\
(1-\epsilon) e^{-\alpha} + \epsilon e^{\alpha} &=0\
\alpha &=\frac {1}{2} log\frac {1-\epsilon}{\epsilon} \
\end {align
}

这样,我们就得到了弱分类器的投票权重和误差点该扩大的比例了。将 ααα 带回刚开始的步骤,AdaBoost 的模型就完善了。

若读者问这样做的 Boosting 达到最优求解了吗?笔者只能说不知道,也不能给出证明,因为 AdaBoost 的两位创始人大牛的确完美的绕过最优化误差的方法,得到了 AdaBoost 的解法。但倘若有人问 AdaBoost 真的有效吗?我可以肯定的回答:非常有效!因为这一算法在训练集上的误差是指数递减的!而且还不怎么过拟合!!举个例子,很多人听过盲人摸象的故事,讲得是四个盲人各自摸了大象的一部分就以为知道大象了,他们有的说大象像柱子,有的说大象像鞭子,有的说大象像管道,还有一个说大象像墙。这些盲人就像一个个弱学习集,AdaBoost 强大之处在于可以把弱学习集认识的大象组合到一起成为一只完整的大象,而且每次组合都保证可以吸收新学习集比之旧学习集的优秀部分!此外还可以做到兼顾宏观,保证学习的是大象的泛化特征,不会因为学习的大象群里有几个缺胳膊少腿的就学错!!
AdaBoost 的错误上限及防过拟合特性会在下一部分详述。

 

3, Boosing 主要优点及解释:

Boosting 的主要优点有:

1, 简单且易于代码实现

2, 不需要做特征筛选

3, 极其灵活,可以与任何其他机器学习算法结合

4, 通过对分类能力稍微强于 1/21/21/2 的若干弱分类器的训练,可以得到能力大大增强的强分类器 (别的学习器也适用)

另外在通常意义 Boosting 的基础上,AdaBoost 的进一步优点有:

5, 无需先验知识(Adaptive Boosting 本来的含义就是自适应性 Boosting)

6, 精度很高

7, 训练得到新分类器在训练集上的误差上限必定会指数速度的下降。

8, 对过拟合(overfitting)敏感,通常迭代次数增加不会造成过拟合

(以下部分了解即可)

AdaBoost 最吸引人的莫过于最后两点特征,其详细解释如下:

1, 误差上限理论。训练集上的误差上限会指数下降

AdaBoost 的误差上限 ∏Zt∏Zt∏Zt 为

1m∑i[[H(xi)≠yi]]≤∏Zt

1m∑i[[H(xi)≠yi]]≤∏Zt1m∑i[[H(xi)≠yi]]≤∏Zt

 

证明如下:

\begin {align}
D{t+1}(i) &=\frac {D{t}(i)\exp (-\alpha t y_i h_t (x_i))}{Z_t}\
&=\frac {\exp (- \sum
{t} \alpha _t y_i h_t (x_i))}{m\Pi_t Z_t}\
&=\frac {\exp (y_i f (x_i))}{m\Pi_t Z_t}
\end {align
}

当 H(xi)≠yiH(xi)≠yiH(xi)≠yi 时,yif(xi)≤0yif(xi)≤0yif(xi)≤0, 则 exp(−yif(xi))>1exp(−yif(xi))>1exp⁡(−yif(xi))>1, 当 H(xi)=yiH(xi)=yiH(xi)=yi 时,yif(xi)≥0yif(xi)≥0yif(xi)≥0, 则 exp(−yif(xi))>0exp(−yif(xi))>0exp⁡(−yif(xi))>0

\begin {align}
[![ H (xi) \neq y_i ]!] &\leq \exp (-y_i f (x_i)) \
\frac {1}{m} \sum
{i} [![ H (xi) \neq y_i ]!] &\leq \frac {1}{m} \sum{i} \exp (-yi f (x_i)) \
&=\sum
{i} (\prod Zt) D{T+1}(i) \
&=\prod Z_t
\end {align
}

桌面.PNG

于是可知,每添加一个分类器,Adaboost 的误差上界会乘以 ZtZtZt 然后,将 αt=12log1−εεαt=12log1−εεαt=12log⁡1−εε 带入最初假设的 ZtZtZt中

\begin {align}
Zt &=-\sum{i=1}^m e^{yi \alpha_i h_t (x_i)}\
&=\sum
{i: h (xi) = yi} D (i) e^{-\alpha} + \sum{i: h (xi) \neq y_i} D (i) e^{\alpha}\
&= (1-\epsilon _t) e^{-\alpha} + \epsilon _t e^{\alpha}\
&=2 \sqrt { \varepsilon _t (1-\varepsilon _t)}
\end {align
}

zt.PNG

所以 Zt=2εt(1−εt)‾‾‾‾‾‾‾‾‾√Zt=2εt(1−εt)‾‾‾‾‾‾‾‾‾√Zt=2εt(1−εt)

对于任何 000 到 111 之间变动的 εεε,ZtZtZt 总是小于等于 111 的,所以只要可以找到新的分类效果稍微比 1/21/21/2 好的分类器,总误差必然会指数下降。

2, 对过拟合敏感

通常来说,根据 VC 维理论,为了提升训练集上的预测精度表现而不断将算法复杂化并不一定能使算法的性能真正提高。初始时学习器通过学习训练集上的重要而泛化的特征的确会使训练集误差和测试集(代表真实情况)误差同时下降,但当泛化的重要特征被学习完,学习器开始学习训练数据的特殊特征时,训练集误差的下降反而伴随着测试集误差的不断上升。如下图所示:

adaboost1.PNG

但 Boosting 的实验情况却并非如此,下面是一个实验情况:

adaboost2.PNG

VC 维理论虽然很好的解释了测试误差在训练误差达到一定程度时会上升,但不能很好解释误差在上升后存在下降的现象,此外比起决策树,AdaBoost 的过拟合情况改善了太多。一些博客上同样引用 Robert 在 1997 年 --1999 年左右发表的文章借用 margin 理论来解释这一现象,但 Robert 的理论总有些自吹的味道。AdaBoost 的是否对过拟合有强壮的(robust)抵抗力这一问题到 2012 年为止在国外很多论坛上仍有人讨论(12 年到现在讨论渐渐消失,一方面大家更倾向与用更新的 Boosting 方法,另一方面新的 Boosting 方法如 XGBoosting,LightGBM 都加了很多正则项,算是默认 Boosting 本身防过拟合还不是非常好)。不过可以确定的是 Boosting 方法比起线性回归或者普通的决策树来说防止过拟合的能力已经有了极大提升。

笔者认为 VC 维理论在 AdaBoost 上也是成立的,但 AdaBoost 没有像 VC 维理论预测的一样发生大幅度过拟合的原因有二:

1, AdaBoost 使用若干数的加权均值,而非最后单一树来进行预测,减轻了过拟合。

虽然在基于上一个分类器错误的基础上建立新的分类器必定存在过拟合状况。但是就像随机森林(random forest)一样,通过对多棵树的综合求解,会大大缓解过拟合情况。

2, AdaBoost 要求改进的分类器正确率 k>1/2k>1/2k>1/2 的分类器。这一要求本身就类似于决策树中的后剪枝,及过拟合部分被截掉了,这注定了 AdaBoost 不会太多的过度生长。


Python 代码应用举例:

本部分以 scikit-learn 示例的 “Two-class AdaBoost” 作为例子来介绍 AdaBoost 的 Python 应用。

首先,所需的 Python 包,本例中使用 AdaBoost 做 Boosting 方法,用决策树(decision tree)做分类器,所以这两个算法的包需要在此导入。若读者习惯用其他模型,也可以把决策树模型换成其他简单的分类模型。需要注意的是基础模型无需太过复杂,只要确保有 1/21/21/2 以上的正确率,AdaBoost 就可以将这些基础模型训练成强学习器。

然后导入数据,在这虚拟产生一些二维数据作为演示,
举例 1.PNG

再之后建立 AdaBoost 模型。AdaBoost 本身不需要太多参数,但基础的分类器需要调整参数。决策树基础的参数含义在上篇 “决策树入门及 Python 里提到过了”,感兴趣的读者可以返回看看。在此使用最大深度为 1 的简单决策树作为基本分类器,AdaBoost 使用方法 “SAMME”(一种 AdaBoost 实现方法),并迭代 200 次。

举例 2.PNG

预测完成,画出预测结果图:

举例 3.PNG
final2.PNG

如上图,AdaBoost 分类效果不错,基本可以将红蓝点区分。

 

补充和扩展:

Boosting 是决策树的重要进阶算法之一(另一种是 Random Forest)。Boosting 算法如今在机器学习领域占有举足轻重的地位,其基础上发展的 XGBoost,LightGBM 更是数据分析网站 Kaggle 上的冠军常客,对这一领域有兴趣的读者可以继续关注本系列的其他文章。

 

 

参考文献 

[1][1][1]Jan Sochman, Jiˇr´ı Matas. “AdaBoost”. Center for Machine Perception Czech Technical University, Prague http://cmp.felk.cvut.cz

[2][2][2]Robert E. Schapire. “Explaining AdaBoost”. http://rob.schapire.net/papers/explaining-adaboost.pdf

本文由 JoinQuant 量化课堂推出,版权归 JoinQuant 所有,商业转载请联系我们获得授权,非商业转载请注明出处。

文章更迭记录:
v1.0,2018-01-05,文章上线

原文链接:

https://www.joinquant.com/view/community/detail/10626

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值