集成学习—Adaboost(理解与应用)

在上一篇集成学习—Adaboost(论文研读)中已经将Adaboost的原始论文精读了一遍,这篇博客主要是对Adaboost算法(主要是二分类的Adaboost)进行更深入的理解和推导,以及尝试下关于它的基于Python的代码的应用。

模型回顾

Adaboost算法,全称为the adaptive boosting algorithm,它是boosting算法中的代表。对于boosting来说,由于它是一个串行生成的集成学习方法,主要有两方面需要考虑:一是在每一轮中如何改变训练数据的权值或者概率分布;二是如何将弱学习器组合成一个强学习器。Adaboost的做法是:提高那些被前一轮弱学习器错误学习的样本的权重,对于那些弱学习器学习的不够好的数据,下一轮的弱学习器会重点学习;弱学习器的组合方式,Adaboost采用的是加权多数表决的方法,即学习错误率小的弱分类器权重较大,在表决中起较大作用,相反错误率大的权重小,作用也相对较小。Adaboost的巧妙之处就在于它将这些想法自然且有效地实现在一种算法中。

在原始的论文中,Freund和Schapire将二分类 { 0 , 1 } \{0,1\} {0,1}问题的Adaboost算法的伪代码描述为:
在这里插入图片描述

简单来说,算法的步骤如下:首先需要对数据样本的权重向量赋初值,一般是设为均匀分布,即 w i 1 = 1 / N , i = 1 , … , N w_i^1=1/N, i=1,\dots,N wi1=1/N,i=1,,N,然后在 t t t次迭代中计算分布 p t p^t pt,对第 t t t个弱学习器赋予这个分布,得到弱学习器的输出假设 h t h_t ht,然后再计算这个假设的(相对于当前分布的)加权错误率 ε t \varepsilon_t εt,令 β t = ε t / ( 1 − ε t ) \beta_t=\varepsilon_t/(1-\varepsilon_t) βt=εt/(1εt),再对下一轮的新权重进行更新,更新规则为 w i t + 1 = w i t β t 1 − ∣ h t ( x i ) − y i ∣ w_i^{t+1}=w_i^t\beta_t^{1-|h_t(x_i)-y_i|} wit+1=witβt1ht(xi)yi,这样经过 T T T次迭代,得到 T T T个弱学习器,最后将他们的结果进行加权,输出最后算法的最终假设 h f ( x ) h_f(x) hf(x),最后 h f ( x ) = 1 h_f(x)=1 hf(x)=1的条件为 ∑ t = 1 T ( l o g 1 / β t ) h t ( x ) ∑ t = 1 T ( l o g 1 / β t ) ⩾ 1 / 2 {\sum_{t=1}^T(log1/\beta_t)h_t(x)\over \sum_{t=1}^T(log1/\beta_t)}\geqslant1/2 t=1T(log1/βt)t=1T(log1/βt)ht(x)1/2即所有弱假设的加权和若大于0.5则判定最后输出的标签为1,否则为0。

总的来看,这个算法的迭代过程主要更新的是数据分布的权重 w w w以及每个弱分类器的权重 l o g ( 1 / β ) log(1/\beta) log(1/β)。具体来说,在弱学习器给出弱假设的结果后,若计算出来的错误率 ε t \varepsilon_t εt较大,则算法第4步中计算出来的 β t \beta_t βt就较大,同时它的精度 1 − ∣ h t ( x i ) − y i ∣ 1-|h_t(x_i)-y_i| 1ht(xi)yi就小,因而权重更新中的系数 β t 1 − ∣ h t ( x i ) − y i ∣ \beta_t^{1-|h_t(x_i)-y_i|} βt1ht(xi)yi就趋于1(较大,因为弱学习器是PAC学习的,所以关于它的误差假设是 ε t ⩽ 1 / 2 − γ \varepsilon_t\leqslant1/2-\gamma εt1/2γ,其中 γ > 0 \gamma>0 γ>0,因此这样定义的 β ∈ [ 0 , 1 ] \beta\in[0,1] β[0,1]),导致下一步更新后的权重较大,从而上一步分类错误的那些数据将在下一个弱学习器中重点学习,由此一点点改善数据的分类错误率;此外,根据每次迭代得到的 β t \beta_t βt值,得到每个弱分类器在最后假设中的权重,错误率较大的弱分类器它的 β t \beta_t βt就较大,因此 l o g ( 1 / β t ) log(1/\beta_t) log(1/βt)较小,意味着它在最后的决策中占得比重较小,最后的结果由 T T T个弱分类器加权投票得到,更多的是依靠分类正确率高的那些弱分类器的信息。

加性模型

Adaboost算法有多种推导方式,周志华老师的《机器学习》上说比较容易理解的是基于“加性模型”,即基学习器的线性组合来最小化指数损失函数的方法,这个方法是由Friedman等人在2000年正式发表,而且这个理解方法是现在大多数学习Adaboost算法接触到的,因此这里也对针对这篇文章进行了简单的理解。

在Additive logistic regression: a statistical view of boosting这篇文章中,作者主要是从统计学的角度出发来解释Adaboost算法,说明了Adaboost实际上是一个加性逻辑回归模型,它用牛顿方法来最优化一个特别的指数损失函数,文章最后还提出了提升决策树(boosting decision tree),但这里不做讨论。

Friedman总结的Adaboost算法如下(实际上与上图的是一样的,除了权重 w w w的更新的系数表示上有些微差别,Freund的权重不需要做归一化处理,但是这里需要):在这里插入图片描述

理解与推导过程:

Adaboost算法是模型为加性模型、损失函数为指数函数、学习算法为前向分步算法(forward stage-wise algorithm)时的二分类学习方法(摘自李航老师的《统计学习方法》)。由上图知道Adaboost最终的分类器为 M M M个弱学习器 f m ( x ) f_m(x) fm(x)的加权投票 F ( x ) = ∑ m = 1 M c m f m ( x ) F(x)=\sum_{m=1}^Mc_mf_m(x) F(x)=m=1Mcmfm(x),它的目标是最小化指数损失函数 E ( e − y F ( x ) ) E(e^{-yF(x)}) E(eyF(x))

因此,论文中首先给出了一个有用的引理:

引理1 E ( e − y F ( x ) ) E(e^{-yF(x)}) E(eyF(x)) F ( x ) = 1 2 l o g P ( y = 1 ∣ x ) P ( y = − 1 ∣ x ) (1) F(x)={1\over2}log{P(y=1|x)\over P(y=-1|x)}\tag{1} F(x)=21logP(y=1x)P(y=1x)(1)处达到最小。因此, P ( y = 1 ∣ x ) = e F ( x ) e − F ( x ) + e F ( x ) ,    P ( y = − 1 ∣ x ) = e − F ( x ) e − F ( x ) + e F ( x ) P(y=1|x)={e^{F(x)}\over e^{-F(x)}+e^{F(x)}},\ \ P(y=-1|x)={e^{-F(x)}\over e^{-F(x)}+e^{F(x)}} P(y=1x)=eF(x)+eF(x)eF(x),  P(y=1x)=eF(x)+eF(x)eF(x)证明:因为在 y y y x x x的联合分布上求期望,可以充分得到在 x x x上的条件期望最小值,所以对 E ( e − y F ( x ) ∣ x ) = P ( y = 1 ∣ x ) e − F ( x ) + P ( y = − 1 ∣ x ) e F ( x ) E(e^{-yF(x)}|x)=P(y=1|x)e^{-F(x)}+P(y=-1|x)e^{F(x)} E(eyF(x)x)=P(y=1x)eF(x)+P(y=1x)eF(x) ∂ E ( e − y F ( x ) ∣ x ) ∂ F ( x ) = − P ( y = 1 ∣ x ) e − F ( x ) + P ( y = − 1 ∣ x ) e F ( x ) {\partial E(e^{-yF(x)}|x)\over \partial F(x)}=-P(y=1|x)e^{-F(x)}+P(y=-1|x)e^{F(x)} F(x)E(eyF(x)x)=P(y=1x)eF(x)+P(y=1x)eF(x)为0即可得到结论。

一般来说逻辑回归模型不像式(1)中有一个 1 2 1\over 2 21的因子,但是通过上下同乘 e F ( x ) e^{F(x)} eF(x)可以得到通常意义下的逻辑回归模型 p ( x ) = e 2 F ( x ) 1 + e 2 F ( x ) p(x)={e^{2F(x)}\over1+e^{2F(x)}} p(x)=1+e2F(x)e2F(x)因此说这两个模型是等价的。这也是为什么作者提出来Adaboost实际上可以解释为加性的逻辑回归模型。然后再给出了解释这个算法过程的命题(命题的证明就是算法的主要推导过程)。

命题1 离散的Adaboost算法产生了自适应的牛顿迭代更新来最小化 E ( e − y F ( x ) ) E(e^{-yF(x)}) E(eyF(x)),这是对一个加性逻辑回归模型的分步贡献。

命题1的证明如下
J ( F ) = E [ e − y F ( x ) ] J(F)=E[e^{-yF(x)}] J(F)=E[eyF(x)],假设我们已经有了一个当前的分类器 F ( x ) F(x) F(x),想要得到一个改进的分类器 F ( x ) + c f ( x ) F(x)+cf(x) F(x)+cf(x),也就是在当前分类器中继续添加一个弱学习器,希望新的弱学习器在上一步更新后的样本分布中能最小化损失函数 J ( F + c f ) J(F+cf) J(F+cf),也就是使前向分步算法(stage-wise)得到的 c c c f f f使 F + c f F+cf F+cf在训练集上的指数损失最小。现在的任务就是证明使得损失函数 J ( F + c f ) J(F+cf) J(F+cf)达到最小的 c ^ \hat c c^ f ^ \hat f f^,就是Adaboost算法所得到的 c c c f f f,证明可以分为两步:

首先,求 f ^ \hat f f^。对于固定的 c c c x x x,我们在 f ( x ) = 0 f(x)=0 f(x)=0处将 J ( F + c f ) J(F+cf) J(F+cf)泰勒展开到二阶: J ( F + c f ) = E [ e − y ( F ( x ) + c f ( x ) ) ] = E [ e − y F ( x ) e − c y f ( x ) ] ≈ E [ e − y F ( x ) ( 1 − c y f ( x ) + c 2 y 2 f 2 ( x ) 2 ) ] = E [ e − y F ( x ) ( 1 − c y f ( x ) + c 2 f 2 ( x ) 2 ) ]    ( 因为 y ∈ { + 1 , − 1 } ) \begin{aligned} J(F+cf)&=E[e^{-y(F(x)+cf(x))}]\\ &=E[e^{-yF(x)}e^{-cyf(x)}]\\ &\approx E[e^{-yF(x)}(1-cyf(x)+{c^2y^2f^2(x)\over2})]\\ &= E[e^{-yF(x)}(1-cyf(x)+{c^2f^2(x)\over2})]\ \ (\text{因为}y\in\{+1,-1\}) \end{aligned} J(F+cf)=E[ey(F(x)+cf(x))]=E[eyF(x)ecyf(x)]E[eyF(x)(1cyf(x)+2c2y2f2(x))]=E[eyF(x)(1cyf(x)+2c2f2(x))]  (因为y{+1,1})关于 f ( x ) ∈ { + 1 , − 1 } f(x)\in\{+1,-1\} f(x){+1,1}逐点最小化,得到最优的弱学习器为: f ^ ( x ) = arg ⁡ min ⁡ f E w [ 1 − c y f ( x ) + c 2 f 2 ( x ) 2 ∣ x ] = arg ⁡ min ⁡ f E w [ ( y − c f ( x ) ) 2 ∣ x ] = arg ⁡ min ⁡ f E w [ ( y − f ( x ) ) 2 ∣ x ]    ( 因为 f ( x ) ∈ { + 1 , − 1 } ) \begin{aligned} \hat{f}(x)&=\arg\min_f E_w[1-cyf(x)+{c^2f^2(x)\over2}|x]\\ &=\arg\min_f E_w[(y-cf(x))^2|x]\\ &=\arg\min_f E_w[(y-f(x))^2|x]\ \ (\text{因为}f(x)\in\{+1,-1\}) \end{aligned} f^(x)=argfminEw[1cyf(x)+2c2f2(x)x]=argfminEw[(ycf(x))2x]=argfminEw[(yf(x))2x]  (因为f(x){+1,1})这里的 w ( y ∣ x ) = e x p ( − y F ( x ) ) / E [ e x p ( − y F ( x ) ) ] w(y|x)=exp(-yF(x))/E[exp(-yF(x))] w(yx)=exp(yF(x))/E[exp(yF(x))],因此这里得到了下一步最佳的弱分类器 f ^ \hat{f} f^

其次,求 c ^ \hat c c^。给定 f ^ ∈ { + 1 , − 1 } \hat{f}\in\{+1,-1\} f^{+1,1},我们就可以直接最小化 J ( F + c f ^ ) = E [ e − y F ( x ) e − c y f ( x ) ] J(F+c\hat{f})=E[e^{-yF(x)}e^{-cyf(x)}] J(F+cf^)=E[eyF(x)ecyf(x)]来求得系数 c c c,因为 e − y F ( x ) e^{-yF(x)} eyF(x)部分与 c c c f ^ \hat{f} f^都无关,因此最小化时可以不用考虑,相当于常数,因此: c ^ = arg ⁡ min ⁡ c E w [ e − c y f ^ ( x ) ] \begin{aligned} \hat{c}&=\arg\min_c E_w[e^{-cy\hat{f}(x)}] \end{aligned} c^=argcminEw[ecyf^(x)]利用引理1的方法可以得到 E ( e − c y f ^ ( x ) ∣ x ) = P ( y = f ^ ∣ x ) e − c + P ( y ≠ f ^ ∣ x ) e c E(e^{-cy\hat{f}(x)}|x)=P(y=\hat{f}|x)e^{-c}+P(y\neq\hat{f}|x)e^{c} E(ecyf^(x)x)=P(y=f^x)ec+P(y=f^x)ec ∂ E ( e − c y f ^ ( x ) ∣ x ) ∂ c = − P ( y = f ^ ∣ x ) e − c + P ( y ≠ f ^ ∣ x ) e c {\partial E(e^{-cy\hat{f}(x)}|x)\over \partial c}=-P(y=\hat{f}|x)e^{-c}+P(y\neq\hat{f}|x)e^{c} cE(ecyf^(x)x)=P(y=f^x)ec+P(y=f^x)ec等于0,即可得 c ^ = 1 2 l o g P ( y = f ^ ∣ x ) P ( y ≠ f ^ ∣ x ) = 1 2 l o g 1 − e e \hat{c}={1\over2}log{P(y=\hat{f}|x)\over P(y\neq\hat{f}|x)}={1\over2}log{1-e\over e} c^=21logP(y=f^x)P(y=f^x)=21loge1e其中 e = P ( y ≠ f ^ ∣ x ) = E w [ 1 ( y ≠ f ^ ) ] e=P(y\neq\hat{f}|x)=E_w[1_{(y\neq\hat{f})}] e=P(y=f^x)=Ew[1(y=f^)]表示错误率,这也是算法图中(b)的那一步的更新规则,因此就得到了最后输出结果 F ( x ) F(x) F(x)的更新规则: F ( x ) ← F ( x ) + 1 2 l o g 1 − e e f ^ ( x ) F(x)\leftarrow F(x)+{1\over2}log{1-e\over e}\hat{f}(x) F(x)F(x)+21loge1ef^(x)

最后,对于权重的更新,由 J ( F + c f ) J(F+cf) J(F+cf)的定义就可以得到更新的规则: w ( y ∣ x ) ← w ( y ∣ x ) ⋅ e − c ^ y f ^ ( x ) w(y|x)\leftarrow w(y|x)\cdot e^{-\hat{c}y\hat{f}(x)} w(yx)w(yx)ec^yf^(x)再加上归一化即可。由于 y f ^ ( x ) = 2 × 1 ( y ≠ f ^ ( x ) ) − 1 y\hat f(x)=2\times1_{(y\neq \hat f(x))}-1 yf^(x)=2×1(y=f^(x))1,因此上面的更新规则等价于: w ( y ∣ x ) ← w ( y ∣ x ) ⋅ e x p ( l o g ( 1 − e e ) 1 ( y ≠ f ^ ( x ) ) ) w(y|x)\leftarrow w(y|x)\cdot exp\bigg(log\big({1-e\over e}\big)1_{(y\neq \hat f(x))}\bigg) w(yx)w(yx)exp(log(e1e)1(y=f^(x)))与给出的Adaboost算法图中的一样。

至此,证明了二分类的Adaboost的过程。

Adaboost算法优缺点:

  1. 运行很快;
  2. 十分灵活,它提供的是一个框架,它的弱学习器可以搭配各种学习算法,例如决策树、SVM等;
  3. 多用途,可以用来做二分类、多分类、回归等,也适用于文本数据、连续数据、离散数据等;
  4. 不需要知道弱学习器的任何先验知识;
  5. 易用,需要调节的参数很少,只有一个迭代次数;
  6. 有效性,训练的错误率随着迭代次数的增加呈指数级下降,因此很快就能达到很好的效果;
  7. 大部分时候能抵抗过拟合。

但是主要的缺点也很明显:

  1. Adaboost的性能取决于数据和使用的弱学习器,如果弱学习器太强可能会导致算法过拟合,太弱可能会欠拟合;
  2. 对异常样本敏感,异常样本在迭代中可能会获得较高的权重,弱学习器在这些样本的关注较多,影响最终的预测准确性。

Python与应用

  • 搭配Adaboost的基学习器一般都选择决策树,此时这样的算法Adaboost-Decision tree,总结起来就是:Adaboost + 根据权重sampling + 剪枝的decision tree(具体可以参考机器学习技法视频课)。使用Adaboost的方法计算每次的数据权重,然后根据这个权重采样出部分的样本给决策树进行学习,这个决策树一般会限制它的高度,让每个弱学习器不要学得太好,最后综合投票这些弱学习器的结果。

  • Python:sklearn.ensemble模块中包含了AdaBoost, 包括用于分类的AdaBoostClassifier以及用于回归的AdaBoostRegressor。由于这里使用的数据集是连续回归问题,因此这里只讨论AdaBoostRegressor,它的算法主要是用Drucker, Harris. “Improving Regressors Using Boosting Techniques.” Fourteenth International Conference on Machine Learning 1997. 的这篇论文中的AdaBoost.R2算法,基本上和Freund提出的AdaBoost.R类似,不过弱学习器默认选择的是CART决策树,使用MSE作为划分准则,具体可以参考之前的博客描述的CART决策树的内容。

  • 数据来源及说明:天池新人赛工业蒸汽量预测,经脱敏后的锅炉传感器采集的数据(采集频率是分钟级别),根据锅炉的工况,预测产生的蒸汽量。数据分成训练数据(train.txt)和测试数据(test.txt),其中字段”V0”-“V37”,这38个字段是作为特征变量,”target”作为目标变量。选手利用训练数据训练出模型,预测测试数据的目标变量,排名结果依据预测结果的MSE(mean square error)。

AdaBoostRegressor某些参数说明,具体可参考文档说明

  • base_estimator:基学习器,可选(默认为回归决策树,max_depth=3,分类的话默认为决策树桩)
  • n_estimators:终止boosting的最大估计数,也就是基学习器的个数。如果完全匹配,则提前停止学习。可选(默认为50)
  • learning_rate:学习率,学习率缩小了每个回归器的贡献,在学习率和学习器的个数之间有一个平衡, 可选(默认为1.)
  • loss:在每次boosting迭代后更新权重时使用的损失函数。可选线性、平方、指数损失函数,即{‘linear’, ‘square’, ‘exponential’},可选(默认为线性损失)
  • X:训练集样本,数组类型的稀疏矩阵,shape = [n_samples, n_features]
  • y:目标变量(实数),数组类型,shape = [n_samples]
  • sample_weight:初始的样本权重,数组类型,shape = [n_samples],可选,默认为 1 / n s a m p l e s 1 / n_{samples} 1/nsamples

代码


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split #划分数据集
from sklearn.metrics import mean_squared_error #MSE,与下面的定义的是一样的

#训练集和测试集导入
training_data = pd.read_csv('zhengqi_train.txt', sep='\t')
test_data = pd.read_csv('zhengqi_test.txt', sep='\t')
training_x = training_data.iloc[:,0:38] #用索引取前38列得到训练数据
training_target = training_data['target'] #最后一列为label
#选出30%为验证集(validation data set)
x_train, x_val, y_train, y_val = train_test_split(training_x, training_target, test_size=0.3, random_state=0)

#mean square error
def MSE(predict_x, y):
    m = len(predict_x)
    return np.sum((predict_x-y)**2)/(m) 

'''============================ Adaboost =============================='''
from sklearn.ensemble import AdaBoostRegressor

#随着参数变化的验证集MSE
def n_estimators(n):
    rg = AdaBoostRegressor(n_estimators = n, loss = 'square')
    rg.fit(x_train, y_train) 
    return MSE(rg.predict(x_val), y_val)

#画图选择参数
a = np.linspace(10, 310, num=31)
b = np.zeros((31,))
for i in range(31):
    b[i,] = n_estimators(int(a[i,]))
plt.scatter(a, b)
plt.plot(a, b)
plt.scatter(a[np.argmin(b),], min(b), c='r') #最优点标红


#用较优的参数来训练模型
best_n = int(a[np.argmin(b),]) #返回最优的参数值
rg = AdaBoostRegressor(n_estimators=best_n, loss='square')
rg.fit(x_train, y_train) 

MSE(rg.predict(x_val), y_val) #验证集MSE为0.15772145020073947


'''保存测试集的预测结果'''
predict_x = pd.DataFrame(rg.predict(test_data))
predict_x.to_csv('/Users/Desktop/Adaboost_results1.txt', header=0, index=0) #不保留列名和索引

根据验证集的MSE变化,选择最优的参数为80
在这里插入图片描述

AdaBoost分类

稍微尝试了下AdaBoostClassifier,这里给出了一个比较直观的例子,由于可视化的原因,这里的数据源采用的是吴恩达|机器学习作业7.0.k-means聚类的数据源,但是因为k-means是无监督学习方法,Adaboost是监督学习方法,这个数据是无标签的数据,因此这里先用k-means聚类的结果当做是这个数据源的标签,然后再改变Adaboost中的弱学习器的个数来观察它的效果。具体代码如下:

import numpy as np
import matplotlib.pyplot as plt
import scipy.io as scio
from sklearn.ensemble import AdaBoostClassifier
from sklearn.cluster import MiniBatchKMeans #Mini Batch K-Means聚类


data = scio.loadmat('C:/Users/dell/Desktop/ex7data2.mat')
x = data['X']


'''可视化数据集'''
plt.scatter(x[:,0], x[:,1], marker='o', c='w', edgecolors='k')


'''聚类得到数据标签'''
K = 3 #设置聚类数量
kmeans = MiniBatchKMeans(K) 
kmeans.fit(x) 

#画出标签之后的数据
def plot_idx(x, idx):
    color = ['r', 'g', 'b'] #颜色序列
    for i in range(3):
        plt.scatter(x[idx==i][:,0], x[idx==i][:,1], marker='o', c='w', edgecolors=color[i-1])

plot_idx(x, kmeans.predict(x))      
  

#得到数据标签
y = kmeans.predict(x)


'''Adaboost分类'''
def plot_process(n):
    clf = AdaBoostClassifier(n_estimators=n)
    clf.fit(x, y)
    plot_idx(x, clf.predict(x))


plot_process(4) #改变这里的弱学习器个数,观察分类效果随学习器个数的变化

首先数据集是这样的:
在这里插入图片描述
然后用k-means聚类得到数据标签:
在这里插入图片描述

最后改变Adaboost中弱学习器的个数,从1到4个,观察算法的学习效果:
在这里插入图片描述在这里插入图片描述
很明显当只有一个弱学习器的时候显然效果不是很好,两个的时候显著提升,从弱学习器个数增加到3个开始,这个算法基本上已经把分类错误的样本全都纠正过来了,但由于这个数据集十分简单,所以学习器的个数不需要很多,在面对复杂的数据集的时候Adaboost表现还是值得期待的。

参考资料:

《机器学习》周志华
《统计学习方法》李航
Friedman J, Hastie T, Tibshirani R. Additive logistic regression: a statistical view of boosting (With discussion and a rejoinder by the authors)[J]. Annals of Statistics, 2000, 28(2):337-374.
Drucker, Harris. “Improving Regressors Using Boosting Techniques.” Fourteenth International Conference on Machine Learning 1997.
机器学习技法视频课

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值