集成学习方法二

请添加图片描述

bagging:

  • 对不同的子集使用同一种基本模型进行拟合,然后投票得出最终的预测

  • 降低方差

boosting:

  • 使用同一组数据集进行反复学习,得到一系列简单模型,然后组合这些模型构成一个预测性能十分强大的机器学习模型

  • 减小偏差

boosting

基本思路

  • 弱学习:识别错误率小于1/2(即准确率仅比随机猜测略高的学习算法)
  • 强学习:识别准确率很高并能在多项式时间内完成的学习算法

在PAC 学习的框架下,强可学习和弱可学习是等价的

从弱学习算法出发,反复学习,得到一系列弱分类器(又称为基本分类器),然后通过一定的形式去组合这些弱分类器构成一个强分类器

大多数的Boosting方法都是通过改变训练数据集的概率分布(训练数据不同样本的权值),针对不同概率分布的数据调用弱分类算法学习一系列的弱分类器

boosting方法要解决的两个问题:

  1. 每一轮学习应该如何改变数据的概率分布?

  2. 如何将各个弱分类器组合起来?

Adaboost算法

基本原理

提高那些被前一轮分类器错误分类的样本的权重,而降低那些被正确分类的样本的权重

各个弱分类器的组合是通过采取加权多数表决的方式,加大分类错误率低的弱分类器的权重,因为这些分类器能更好地完成分类任务,而减小分类错误率较大的弱分类器的权重,使其在表决中起较小的作用

算法步骤

给定一个二分类的训练数据集: T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\} T={(x1,y1),(x2,y2),,(xN,yN)},其中每个样本点由特征与类别组成。特征 x i ∈ X ⊆ R n x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n} xiXRn,类别 y i ∈ Y = { − 1 , + 1 } y_{i} \in \mathcal{Y}=\{-1,+1\} yiY={1,+1} X \mathcal{X} X是特征空间,$ \mathcal{Y} 是 类 别 集 合 , 输 出 最 终 分 类 器 是类别集合,输出最终分类器 G(x)$

(1) 初始化训练数据的分布: D 1 = ( w 11 , ⋯   , w 1 i , ⋯   , w 1 N ) , w 1 i = 1 N , i = 1 , 2 , ⋯   , N D_{1}=\left(w_{11}, \cdots, w_{1 i}, \cdots, w_{1 N}\right), \quad w_{1 i}=\frac{1}{N}, \quad i=1,2, \cdots, N D1=(w11,,w1i,,w1N),w1i=N1,i=1,2,,N (训练数据的权值分布是均匀分布——为了使得第一次没有先验信息的条件下每个样本在基本分类器的学习中作用一样)

(2) 对于m=1,2,…,M

  • 使用具有权值分布 D m D_m Dm训练数据集进行学习,得到基本分类器: G m ( x ) : X → { − 1 , + 1 } G_{m}(x): \mathcal{X} \rightarrow\{-1,+1\} Gm(x):X{1,+1}
  • 计算 G m ( x ) G_m(x) Gm(x)训练集上的分类误差率 e m = ∑ i = 1 N P ( G m ( x i ) ≠ y i ) = ∑ i = 1 N w m i I ( G m ( x i ) ≠ y i ) e_{m}=\sum_{i=1}^{N} P\left(G_{m}\left(x_{i}\right) \neq y_{i}\right)=\sum_{i=1}^{N} w_{m i} I\left(G_{m}\left(x_{i}\right) \neq y_{i}\right) em=i=1NP(Gm(xi)=yi)=i=1NwmiI(Gm(xi)=yi)
  • 计算 G m ( x ) G_m(x) Gm(x)的系数** α m = 1 2 log ⁡ 1 − e m e m \alpha_{m}=\frac{1}{2} \log \frac{1-e_{m}}{e_{m}} αm=21logem1em**,这里的log是自然对数ln (表示了 G m ( x ) G_m(x) Gm(x)在最终分类器的重要性程度) ,当 e m ⩽ 1 2 e_{m} \leqslant \frac{1}{2} em21时, α m ⩾ 0 \alpha_{m} \geqslant 0 αm0,并且 α m \alpha_m αm随着 e m e_m em的减少而增大,因此分类错误率越小的基本分类器在最终分类器的作用越大
  • 更新训练数据集的权重分布
    D m + 1 = ( w m + 1 , 1 , ⋯   , w m + 1 , i , ⋯   , w m + 1 , N ) w m + 1 , i = w m i Z m exp ⁡ ( − α m y i G m ( x i ) ) , i = 1 , 2 , ⋯   , N \begin{array}{c} D_{m+1}=\left(w_{m+1,1}, \cdots, w_{m+1, i}, \cdots, w_{m+1, N}\right) \\ w_{m+1, i}=\frac{w_{m i}}{Z_{m}} \exp \left(-\alpha_{m} y_{i} G_{m}\left(x_{i}\right)\right), \quad i=1,2, \cdots, N \end{array} Dm+1=(wm+1,1,,wm+1,i,,wm+1,N)wm+1,i=Zmwmiexp(αmyiGm(xi)),i=1,2,,N
    样本权重的更新为:
    w m + 1 , i = { w m i Z m e − α m , G m ( x i ) = y i w m i Z m e α m , G m ( x i ) ≠ y i w_{m+1, i}=\left\{\begin{array}{ll} \frac{w_{m i}}{Z_{m}} \mathrm{e}^{-\alpha_{m}}, & G_{m}\left(x_{i}\right)=y_{i} \\ \frac{w_{m i}}{Z_{m}} \mathrm{e}^{\alpha_{m}}, & G_{m}\left(x_{i}\right) \neq y_{i} \end{array}\right. wm+1,i={Zmwmieαm,Zmwmieαm,Gm(xi)=yiGm(xi)=yi

这里的 Z m Z_m Zm是规范化因子,使得 D m + 1 D_{m+1} Dm+1称为概率分布, Z m = ∑ i = 1 N w m i exp ⁡ ( − α m y i G m ( x i ) ) Z_{m}=\sum_{i=1}^{N} w_{m i} \exp \left(-\alpha_{m} y_{i} G_{m}\left(x_{i}\right)\right) Zm=i=1Nwmiexp(αmyiGm(xi))

可以看出,被基本分类器 G m ( x ) G_m(x) Gm(x)错误分类的样本的权重扩大,被正确分类的样本权重减少,二者相比相差 e 2 α m = 1 − e m e m \mathrm{e}^{2 \alpha_{m}}=\frac{1-e_{m}}{e_{m}} e2αm=em1em

(3) 构建基本分类器的线性组合 f ( x ) = ∑ m = 1 M α m G m ( x ) f(x)=\sum_{m=1}^{M} \alpha_{m} G_{m}(x) f(x)=m=1MαmGm(x)——加权表决,得到最终的分类器

G ( x ) = sign ⁡ ( f ( x ) ) = sign ⁡ ( ∑ m = 1 M α m G m ( x ) ) \begin{aligned} G(x) &=\operatorname{sign}(f(x)) \\ &=\operatorname{sign}\left(\sum_{m=1}^{M} \alpha_{m} G_{m}(x)\right) \end{aligned} G(x)=sign(f(x))=sign(m=1MαmGm(x))

向前分步算法

给定数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\} T={(x1,y1),(x2,y2),,(xN,yN)} x i ∈ X ⊆ R n x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n} xiXRn y i ∈ Y = { + 1 , − 1 } y_{i} \in \mathcal{Y}=\{+1,-1\} yiY={+1,1}。损失函数 L ( y , f ( x ) ) L(y, f(x)) L(y,f(x)),基函数集合 { b ( x ; γ ) } \{b(x ; \gamma)\} {b(x;γ)},我们需要输出加法模型 f ( x ) f(x) f(x)

  • 初始化: f 0 ( x ) = 0 f_{0}(x)=0 f0(x)=0

  • 对m = 1,2,…,M:

    • (a) 极小化损失函数:
      ( β m , γ m ) = arg ⁡ min ⁡ β , γ ∑ i = 1 N L ( y i , f m − 1 ( x i ) + β b ( x i ; γ ) ) \left(\beta_{m}, \gamma_{m}\right)=\arg \min _{\beta, \gamma} \sum_{i=1}^{N} L\left(y_{i}, f_{m-1}\left(x_{i}\right)+\beta b\left(x_{i} ; \gamma\right)\right) (βm,γm)=argβ,γmini=1NL(yi,fm1(xi)+βb(xi;γ))
      得到参数 β m \beta_{m} βm γ m \gamma_{m} γm
    • (b) 更新:

    f m ( x ) = f m − 1 ( x ) + β m b ( x ; γ m ) f_{m}(x)=f_{m-1}(x)+\beta_{m} b\left(x ; \gamma_{m}\right) fm(x)=fm1(x)+βmb(x;γm)

  • 得到加法模型:
    f ( x ) = f M ( x ) = ∑ m = 1 M β m b ( x ; γ m ) f(x)=f_{M}(x)=\sum_{m=1}^{M} \beta_{m} b\left(x ; \gamma_{m}\right) f(x)=fM(x)=m=1Mβmb(x;γm)

这样,前向分步算法将同时求解从m=1到M的所有参数 β m \beta_{m} βm γ m \gamma_{m} γm的优化问题简化为逐次求解各个 β m \beta_{m} βm γ m \gamma_{m} γm的问题

梯度提升决策树(GBDT)

回归树中的样本标签是连续数值,可划分点包含了所有特征的所有可取的值。所以再使用熵之类的指标不再合适,取而代之的是平方误差,它能很好的评判拟合程度。

每个样本的残差表示每次使用基函数预测时没有解决的那部分问题

提升树算法

输入数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } , x i ∈ X ⊆ R n , y i ∈ Y ⊆ R T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\}, x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n}, y_{i} \in \mathcal{Y} \subseteq \mathbf{R} T={(x1,y1),(x2,y2),,(xN,yN)},xiXRn,yiYR,输出最终的提升树 f M ( x ) f_{M}(x) fM(x)

  • 初始化 f 0 ( x ) = 0 f_0(x) = 0 f0(x)=0
  • 对m = 1,2,…,M:
    • 计算每个样本的残差: r m i = y i − f m − 1 ( x i ) , i = 1 , 2 , ⋯   , N r_{m i}=y_{i}-f_{m-1}\left(x_{i}\right), \quad i=1,2, \cdots, N rmi=yifm1(xi),i=1,2,,N
    • 拟合残差 r m i r_{mi} rmi学习一棵回归树,得到 T ( x ; Θ m ) T\left(x ; \Theta_{m}\right) T(x;Θm)
    • 更新 f m ( x ) = f m − 1 ( x ) + T ( x ; Θ m ) f_{m}(x)=f_{m-1}(x)+T\left(x ; \Theta_{m}\right) fm(x)=fm1(x)+T(x;Θm)
  • 得到最终的回归问题的提升树: f M ( x ) = ∑ m = 1 M T ( x ; Θ m ) f_{M}(x)=\sum_{m=1}^{M} T\left(x ; \Theta_{m}\right) fM(x)=m=1MT(x;Θm)

梯度提升算法

梯度提升算法(gradient boosting),这是利用最速下降法的近似方法,利用损失函数的负梯度在当前模型的值 − [ ∂ L ( y , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) -\left[\frac{\partial L\left(y, f\left(x_{i}\right)\right)}{\partial f\left(x_{i}\right)}\right]_{f(x)=f_{m-1}(x)} [f(xi)L(y,f(xi))]f(x)=fm1(x)作为回归问题提升树算法中的残差的近似值,拟合回归树。与其说负梯度作为残差的近似值,不如说残差是负梯度的一种特例。
以下开始具体介绍梯度提升算法:
输入训练数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } , x i ∈ X ⊆ R n , y i ∈ Y ⊆ R T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\}, x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n}, y_{i} \in \mathcal{Y} \subseteq \mathbf{R} T={(x1,y1),(x2,y2),,(xN,yN)},xiXRn,yiYR和损失函数 L ( y , f ( x ) ) L(y, f(x)) L(y,f(x)),输出回归树 f ^ ( x ) \hat{f}(x) f^(x)

  • 初始化 f 0 ( x ) = arg ⁡ min ⁡ c ∑ i = 1 N L ( y i , c ) f_{0}(x)=\arg \min _{c} \sum_{i=1}^{N} L\left(y_{i}, c\right) f0(x)=argminci=1NL(yi,c)
  • 对于m=1,2,…,M:
    • 对i = 1,2,…,N计算: r m i = − [ ∂ L ( y i , f ( x i ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) r_{m i}=-\left[\frac{\partial L\left(y_{i}, f\left(x_{i}\right)\right)}{\partial f\left(x_{i}\right)}\right]_{f(x)=f_{m-1}(x)} rmi=[f(xi)L(yi,f(xi))]f(x)=fm1(x)
    • r m i r_{mi} rmi拟合一个回归树,得到第m棵树的叶结点区域 R m j , j = 1 , 2 , ⋯   , J R_{m j}, j=1,2, \cdots, J Rmj,j=1,2,,J
    • 对j=1,2,…J,计算: c m j = arg ⁡ min ⁡ c ∑ x i ∈ R m j L ( y i , f m − 1 ( x i ) + c ) c_{m j}=\arg \min _{c} \sum_{x_{i} \in R_{m j}} L\left(y_{i}, f_{m-1}\left(x_{i}\right)+c\right) cmj=argmincxiRmjL(yi,fm1(xi)+c)
    • 更新 f m ( x ) = f m − 1 ( x ) + ∑ j = 1 J c m j I ( x ∈ R m j ) f_{m}(x)=f_{m-1}(x)+\sum_{j=1}^{J} c_{m j} I\left(x \in R_{m j}\right) fm(x)=fm1(x)+j=1JcmjI(xRmj)
  • 得到回归树: f ^ ( x ) = f M ( x ) = ∑ m = 1 M ∑ j = 1 J c m j I ( x ∈ R m j ) \hat{f}(x)=f_{M}(x)=\sum_{m=1}^{M} \sum_{j=1}^{J} c_{m j} I\left(x \in R_{m j}\right) f^(x)=fM(x)=m=1Mj=1JcmjI(xRmj)

梯度提升算法讲解

XGboost算法

XGboost算法详解

作业

1.Adaboost的基本思路

Adaboost是在初始化权重都相同的数据集下进行学习得到基本学习器,然后提高错误分类样本的权重,减小正确分类样本的权重,得到新的权重分布,然后循环操作,最后实现对多个基本分类器的加权表决。

2.Adaboost和GBDT的联系与区别

联系:

AdaBoost和Gradient Boosting每次基于先前模型的表现选择一个表现一般的新模型并且进行调整,两者都是基于加法模型前向分步算法

区别:

AdaBoost是通过提升错分数据点的权重来定位模型的不足

Gradient Boosting是通过算梯度(gradient)来定位模型的不足,Gradient Boosting可以使用更多种类的目标函数,而当目标函数是均方误差时,计算损失函数的负梯度值在当前模型的值即为残差

3.boosting与bagging的区别,以及如何提升模型的精度?

bagging:

  • 对不同的子集使用同一种基本模型进行拟合,然后投票得出最终的预测

  • 降低方差

boosting:

  • 使用同一组数据集进行反复学习,得到一系列简单模型,然后组合这些模型构成一个预测性能十分强大的机器学习模型

  • 减小偏差

4.使用基本分类模型和Boosting提升模型,画出他们的决策边界

使用Adaboost算法根据葡萄酒数据集预测红酒属于哪一个类别

加载数据
# 引入数据科学相关工具包:
import numpy as np
import pandas as pd 
import matplotlib.pyplot as plt
plt.style.use("ggplot")
%matplotlib inline
import seaborn as sns
# 加载训练数据:         
wine = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data",header=None)

wine.columns = ['Class label', 'Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash','Magnesium', 'Total phenols','Flavanoids', 'Nonflavanoid phenols', 
                'Proanthocyanins','Color intensity', 'Hue','OD280/OD315 of diluted wines','Proline']
# 数据查看:
print("Class labels:",np.unique(wine["Class label"]))#有三类葡萄酒
wine.head()
Class labels: [1 2 3]
Class labelAlcoholMalic acidAshAlcalinity of ashMagnesiumTotal phenolsFlavanoidsNonflavanoid phenolsProanthocyaninsColor intensityHueOD280/OD315 of diluted winesProline
0114.231.712.4315.61272.803.060.282.295.641.043.921065
1113.201.782.1411.21002.652.760.261.284.381.053.401050
2113.162.362.6718.61012.803.240.302.815.681.033.171185
3114.371.952.5016.81133.853.490.242.187.800.863.451480
4113.242.592.8721.01182.802.690.391.824.321.042.93735
数据预处理
# 数据预处理
# 仅仅考虑2,3类葡萄酒,去除1类
wine = wine[wine['Class label'] != 1]
y = wine['Class label'].values
#仅仅考虑两个特征“Alcohol”和“OD280/OD315 of diluted wines”
X = wine[['Alcohol','OD280/OD315 of diluted wines']].values
# print(X,y)

# 将分类标签变成二进制编码:
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
y = le.fit_transform(y)

# 按8:2分割训练集和测试集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=1,stratify=y)  # stratify参数代表了按照y的类别等比例抽样
使用单一决策树建模

DecisionTreeClassifier——决策树分类器

参数:

  • criterion:特征选择标准,有“gini”和“entropy”,默认为“gini”
  • splitter:特征划分标准,有“best”和“random”,"best"适合样本量不大,"random"适合样本量非常大
  • max_depth:决策树的深度
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion='entropy',random_state=1,max_depth=1)
from sklearn.metrics import accuracy_score
tree = tree.fit(X_train,y_train)
y_train_pred = tree.predict(X_train) # 根据决策树模型用训练集预测
y_test_pred = tree.predict(X_test) # 根据决策树模型用测试集预测
tree_train = accuracy_score(y_train,y_train_pred)
tree_test = accuracy_score(y_test,y_test_pred)
print('Decision tree train/test accuracies %.3f/%.3f' % (tree_train,tree_test))
Decision tree train/test accuracies 0.916/0.875
实现Adaboost(基分类器为决策树)

参数:

  • base_estimator:基分类器,默认是DecisionTreeClassifier(max_depth=1)
  • n_estimators:终止学习时的迭代次数,完全拟合时会提前结束迭代
  • learning_rate:学习率
  • algorithm:训练算法{‘SAMME’,‘SAMME.R’},默认=‘SAMME.R’,两者的区别主要是弱学习器权重的度量,前者是对样本集预测错误的概率进行划分的,后者是对样本集的预测错误的比例,即错分率进行划分的
  • random_state:随机种子
from sklearn.ensemble import AdaBoostClassifier
ada = AdaBoostClassifier(base_estimator=tree,n_estimators=500,learning_rate=0.1,random_state=1)
ada = ada.fit(X_train,y_train)
y_train_pred = ada.predict(X_train)
y_test_pred = ada.predict(X_test)
ada_train = accuracy_score(y_train,y_train_pred)
ada_test = accuracy_score(y_test,y_test_pred)
print('Adaboost train/test accuracies %.3f/%.3f' % (ada_train,ada_test))
Adaboost train/test accuracies 1.000/0.917
画出单层决策树与Adaboost的决策边界
x_min = X_train[:, 0].min() - 1
x_max = X_train[:, 0].max() + 1
y_min = X_train[:, 1].min() - 1
y_max = X_train[:, 1].max() + 1
print(x_min,x_max,y_min,y_max)
print("----------------")
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),np.arange(y_min, y_max, 0.1))
# print(xx)
print("----------------")
# print(yy)
print("----------------")
print(xx.shape,yy.shape)
print("----------------")
x_train_fake = np.c_[xx.ravel(), yy.ravel()]#这个操作可以把平面上的点都表示出来,继而表示整个平面
# print(x_train_fake)
print("----------------")
# print(x_train_fake.shape)
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-3-3c4fe75b2756> in <module>
----> 1 x_min = X_train[:, 0].min() - 1
      2 x_max = X_train[:, 0].max() + 1
      3 y_min = X_train[:, 1].min() - 1
      4 y_max = X_train[:, 1].max() + 1
      5 print(x_min,x_max,y_min,y_max)


NameError: name 'X_train' is not defined
# z_fake = tree.predict(x_train_fake)
# z_fake = z_fake.reshape(xx.shape)
# z_fake
# print(X_train.dtype)
# print(y_train)
# print(X_train[y_train==0])
print(X_train[y_train==0,0])# y_train == 0是取的特定的行,0是取的第一列
[12.33 11.56 12.07 11.96 12.16 12.37 12.67 12.37 12.08 12.37 13.03 11.87
 12.08 12.69 11.41 12.08 12.04 12.42 12.21 12.72 12.7  13.49 12.29 12.42
 12.72 12.08 12.08 12.22 12.34 12.6  12.25 12.29 11.79 12.29 12.29 11.03
 12.37 11.65 11.64 11.82 12.   12.   12.52 11.45 11.82 12.99 11.84 12.43
 11.76 13.05 11.46 13.67 12.77 12.64 11.62 12.42 12.47]
f, axarr = plt.subplots(nrows=1, ncols=2,sharex='col',sharey='row',figsize=(12, 6))
for index, clf, title in zip([0, 1],[tree, ada],['Decision tree', 'Adaboost']):
    clf.fit(X_train, y_train)
    # np.c_按行连接矩阵,当传入一维数组时,将一维数组作为列放入到二维数组中,np.r_按列连接矩阵,当传入一维数组时,将两个一维数组连接
    # ravel()实现多维数组扁平化,降为一维数组
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])# 对平面上的所有点都进行预测
    Z = Z.reshape(xx.shape)
    axarr[index].contourf(xx, yy, Z, alpha=0.2)
    axarr[index].scatter(X_train[y_train==0, 0],X_train[y_train==0, 1],c='blue', marker='^')
    axarr[index].scatter(X_train[y_train==1, 0],X_train[y_train==1, 1],c='red', marker='o')
    axarr[index].set_title(title)
axarr[0].set_ylabel('Alcohol', fontsize=12)
plt.tight_layout()
plt.text(0, -0.2,s='OD280/OD315 of diluted wines',ha='center',va='center',fontsize=12,transform=axarr[1].transAxes)
plt.show()

(xx, yy, Z, alpha=0.2)
axarr[index].scatter(X_train[y_train0, 0],X_train[y_train0, 1],c=‘blue’, marker=’^’)
axarr[index].scatter(X_train[y_train1, 0],X_train[y_train1, 1],c=‘red’, marker=‘o’)
axarr[index].set_title(title)
axarr[0].set_ylabel(‘Alcohol’, fontsize=12)
plt.tight_layout()
plt.text(0, -0.2,s=‘OD280/OD315 of diluted wines’,ha=‘center’,va=‘center’,fontsize=12,transform=axarr[1].transAxes)
plt.show()

图片

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值