机器学习—集成算法

集成算法

  • 目的:让机器学习效果更好,单个不行,多个并行一起。
  • Bagging:训练多个分类器取平均 f ( x ) = 1 M ∑ m = 1 M f m ( x ) f(x) = \frac{1}{M} \sum^{M}_{m=1}f_m(x) f(x)=M1m=1Mfm(x)
  • Boosting:从弱学习器开始加强,通过加权进行训练。(提升算法)

加入一棵树,要比原来强。

  • Stacking:聚合多个分类或回归模型(可以分阶段来做)。

随机森林

  • Bootstrap aggregation(并行训练一堆分类器)
  • 随机:选取随机数据、随机特征。

在这里插入图片描述

通过二重随机性,即数据和特征都是随机的,构造出来的每棵树基本都会不一样,结果也会不一样。

  • 森林:很多个决策树并行放在一起。

分类任务中,根据每棵树的结果,以结果相同最多的作为最终结果。

回归任务中,将每棵树进行求和,再求平均作为最终结果。

  • 优势
  • 能够处理很高维度(feature很多)的数据,并且不用做特征选择。

  • 在训练完后,能够给出哪些特征比较重要。

    • 如何选出重要特征?可以通过对比实验:

      如模型1选取特征ABCD,模型2选取特征AB’CD,然后比较准确率。(B’是打乱或者加入异常值的数据)

      模型1.err ≈ 模型2.err,则B特征不重要;

      模型1.err < 模型2.err,则B特征重要。

  • 容易做成并行化方法,速度比较快。

  • 可以进行可视化展示,利与分析。

  • 理论上越多的树效果会越好,但实际上超过一定数量后效果浮动不会发生太大变化。

在这里插入图片描述

提升算法(Boosting)

F m ( x ) = F m − 1 ( x ) + a r g m i n h ∑ i = 1 n L ( y i , F m − 1 ( x i ) + h ( x i ) ) F_m(x) = F_{m-1}(x) + argmin_h\sum^{n}_{i=1}L(y_i,F_{m-1}(x_i)+h(x_i)) Fm(x)=Fm1(x)+argminhi=1nL(yi,Fm1(xi)+h(xi))

前一次的树结果+当前一次的树结果;加入的一棵树要比原来的强。(判断损失是否下降)

  • 典型代表:AdaBoost,Xgboost

AdaBoost会根据前一次的分类效果调整数据权重。

解释:如果某一数据在这次分类错误了,那么在下一次就会赋予它更大的权重。

最终的结果:每个分类器根据自身的准确性来确定各自的权重,再合体。

Stacking(少用)

  • 堆叠:用多个分类器来解决问题。
  • 分阶段:第一阶段得出各自结果,第二阶段再用上一阶段结果训练。

构建数据集

# 导入数据集
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_moons

X,y = make_moons(n_samples=500,noise=0.30,random_state=42)
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=42)
plt.plot(X[:,0][y==0],X[:,1][y==0],'yo',alpha = 0.6)
plt.plot(X[:,0][y==0],X[:,1][y==1],'bs',alpha = 0.6)
plt.show()

在这里插入图片描述

投票策略:软投票与硬投票

  • 硬投票:直接使用类别值,少数服从多数。
# 导入随机森林和投票器
from sklearn.ensemble import RandomForestClassifier,VotingClassifier
# 导入其他分类器
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC

# 实例化
log_clf = LogisticRegression()
rnd_clf = RandomForestClassifier()
svm_clf = SVC()

voting_clf = VotingClassifier(estimators=[('lr',log_clf),('rf',rnd_clf),('svc',svm_clf)],voting='hard') # 传入分类器,list结构;投票方式,当前为硬

# 训练
voting_clf.fit(X_train,y_train)

在这里插入图片描述

# 导入准确率作为评估标准
from sklearn.metrics import accuracy_score

for clf in (log_clf,rnd_clf,svm_clf,voting_clf):
    clf.fit(X_train,y_train)
    y_pred = clf.predict(X_test) # 预测值
    # 打印单个分类器的准确率
    print(clf.__class__.__name__,accuracy_score(y_test,y_pred))
LogisticRegression 0.864
RandomForestClassifier 0.896
SVC 0.896
VotingClassifier 0.904

随机森林通过硬投票后,提升到了0.904,比之前单个分类器效果都要好。

  • 软投票:各自分类器的概率值进行加权平均。
# 实例化
log_clf = LogisticRegression()
rnd_clf = RandomForestClassifier()
svm_clf = SVC(probability=True) # 可获取概率值

voting_clf = VotingClassifier(estimators=[('lr',log_clf),('rf',rnd_clf),('svc',svm_clf)],voting='soft') 

软投票只需要将voting改为soft,打印准确率结果为:

LogisticRegression 0.864
RandomForestClassifier 0.896
SVC 0.896
VotingClassifier 0.92

软投票的效果要比硬投票效果好。

要进行软投票的前提是分类器都需要得到概率值。

Bagging策略

  • 首先对训练集进行多次采样,保证每次得到的采样数据都是不同的。
  • 对采样数据分别训练多个模型,例如树模型。
  • 预测时需要得到所有模型结果再进行集成。

在这里插入图片描述

  • 对比实验,Bagging和决策树
# 导入bagging和决策树
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier

bag_clf = BaggingClassifier(DecisionTreeClassifier(), # 分类器
                  n_estimators=500, # 树的数量
                 max_samples=100,# 最大样本数
                 bootstrap = True,# 随机采样
                 n_jobs = -1, # 多线程  
                 random_state = 42
                ) 
# 训练
bag_clf.fit(X_train,y_train)
# 预测
y_pred = bag_clf.predict(X_test)
# 准确率
accuracy_score(y_test,y_pred)
# 0.904
# 对比实验,单用决策树
ree_clf = DecisionTreeClassifier(random_state=42)
tree_clf.fit(X_train,y_train)
y_pred_tree = tree_clf.predict(X_test)
accuracy_score(y_test,y_pred_tree)
# 0.856

使用Bagging算法的评估为0.904,明显优于单用决策树的评估0.856。

决策边界

  • 集成与传统方法对比
from matplotlib.colors import ListedColormap

# 画图:决策边界
def plot_decasion_boundary(clf,X,y,axes=[-1.5,2.5,-1,1.5],alpha=0.5,contour = True): # axes:范围;alpha:透明度;contour:轮廓
    # 取值范围
    x1s = np.linspace(axes[0],axes[1],100)
    x2s = np.linspace(axes[2],axes[3],100)
    x1,x2 = np.meshgrid(x1s,x2s) # 生成网格点
    X_new = np.c_[x1.ravel(),x2.ravel()] # 拉长,拼接
    y_pred = clf.predict(X_new).reshape(x1.shape) # 预测值,z
    
    # 用不同颜色表达分类边界
    custom_cmap = ListedColormap(['#fafab0','#9898ff','#a0faa0'])
    plt.contourf(x1,x2,y_pred,cmap = custom_cmap,alpha=0.3)
    
    # 等高线
    if contour:
        # 生成颜色
        custom_cmap2 = ListedColormap(['#7d7d58','#4c4c7f','#507d50'])
        plt.contour(x1,x2,y_pred,cmap = custom_cmap2,alpha=0.8)
    # 数据集
    plt.plot(X[:,0][y==0],X[:,1][y==0],'yo',alpha = 0.6)
    plt.plot(X[:,0][y==0],X[:,1][y==1],'bs',alpha = 0.6) 
    plt.axis(axes)
    plt.xlabel('x1')
    plt.ylabel('x2')
# 对比实验
plt.figure(figsize=(12,5))
plt.subplot(121)
plot_decasion_boundary(tree_clf,X,y)
plt.title('Decision Tree')

plt.subplot(122)
plot_decasion_boundary(bag_clf,X,y)
plt.title('Decision Tree With Bagging ')

在这里插入图片描述

左图为单一决策树算法;右图为集成算法,决策边界更平滑,过拟合风险更低。

OOB策略

  • m个训练样本会通过bootstrap (有放回的随机抽样) 的抽样方式进行T次抽样每次抽样产生样本数为m的采样集,进入到并行的T个决策树中。这样有放回的抽样方式会导致有部分训练集中的样本(约36.8%)未进入决策树的采样集中,而这部分未被采集的的样本就是袋外数据oob。

  • 而这个袋外数据就可以用来检测模型的泛化能力,和交叉验证类似。可以理解成从train datasets 中分出来的validation datasets。

bag_clf = BaggingClassifier(DecisionTreeClassifier(), # 分类器
                  n_estimators=500, # 树的数量
                 max_samples=100,# 最大样本数
                 bootstrap = True,# 随机采样
                 n_jobs = -1, # 多线程  
                 random_state = 42,
                 oob_score = True # 使用oob
                ) 
bag_clf.fit(X_train,y_train)
# 验证集评估
bag_clf.oob_score_
# 0.9253333333333333
y_pred = bag_clf.predict(X_test)
accuracy_score(y_test,y_pred)
# 0.904

一般情况下,验证集会比测试集的得分高一点。

# 属于各个类别的概率值
bag_clf.oob_decision_function_

特征重要性热度图展示

  • 一个数据集中往往有成百上千个特征,如何在其中选择比结果影响最大的那几个特征,以此来缩减建立模型时的特征数是我们比较关心的问题。
  • 用随机森林进行特征重要性评估的思想其实很简单,说白了就是看看每个特征在随机森林中的每颗树上做了多大的贡献,然后取个平均值,最后比一比特征之间的贡献大小。
# 导入数据集
from sklearn.datasets import load_iris
iris = load_iris()
rf_clf = RandomForestClassifier(n_estimators=500,n_jobs=-1)
rf_clf.fit(iris['data'],iris['target'])
# 打印特征重要性
for name,score in zip(iris['feature_names'],rf_clf.feature_importances_):
    print(name,score)
sepal length (cm) 0.11105536416721994
sepal width (cm) 0.02319505364393038
petal length (cm) 0.44036215067701534
petal width (cm) 0.42538743151183406
  • 用热度图来表示重要性
from sklearn.datasets import fetch_mldata
mnist = fetch_mldata('MNIST original')
rf_clf = RandomForestClassifier(n_estimators=500,n_jobs=-1)
rf_clf.fit(mnist['data'],mnist['target'])

rf_clf.feature_importances_.shape # 把每个像素当成一个特征
# (784,)
# 热度图
def plot_digit(data):
    image = data.reshape(28,28)
    plt.imshow(image,cmap=matplotlib.cm.hot)
    plt.axis('off') # 不显示坐标轴
plot_digit(rf_clf.feature_importances_)
char = plt.colorbar(ticks = [rf_clf.feature_importances_.min(),rf_clf.feature_importances_.max()]) # 深浅度
char.ax.set_yticklabels(['Not important','Very important'])

在这里插入图片描述

热度图中,颜色越浅表示特征越重要。

Adaboost算法概述

  • Boosting算法的工作机制

    首先从训练集用初始权重训练出一个弱学习器1,根据弱学习的学习误差率表现来更新训练样本的权重,使得之前弱学习器1学习误差率高的训练样本点的权重变高,使得这些误差率高的点在后面的弱学习器2中得到更多的重视。

    然后基于调整权重后的训练集来训练弱学习器2.,如此重复进行,直到弱学习器数达到事先指定的数目T,最终将这T个弱学习器通过集合策略进行整合,得到最终的强学习器。

  • 以SVM分类器为例演示AdaBoost的基本策略
m = len(X_train) # 样本数量

# 不同调节程度对结果的影响
plt.figure(figsize=(14,5))
for subplot,learning_rate in ((121,1),(122,0.5)):
    sample_weights = np.ones(m) # 初始化权重
    plt.subplot(subplot)
    for i in range(5):
        svm_clf = SVC(kernel='rbf',C=0.05,random_state=42) 
        svm_clf.fit(X_train,y_train,sample_weight=sample_weights) # 加入权重项
        y_pred = svm_clf.predict(X_train) # 预测值
        # 改变权重
        sample_weights[y_pred != y_train] *= (1+learning_rate) # 分类错误,加重权重
        # 决策边界
        plot_decasion_boundary(svm_clf,X,y,alpha=0.2)
        plt.title('learning_rate = {}'.format(learning_rate))
    if subplot == 121:
        plt.text(-0.7, -0.65, "1", fontsize=14)
        plt.text(-0.6, -0.10, "2", fontsize=14)
        plt.text(-0.5,  0.10, "3", fontsize=14)
        plt.text(-0.4,  0.55, "4", fontsize=14)
        plt.text(-0.3,  0.90, "5", fontsize=14)
plt.show()

在这里插入图片描述

左图中,学习率较大时,每一次给的权重也会变大;

  • 可以直接导入AdaBoost模型
# 直接导入AdaBoost模型
from sklearn.ensemble import AdaBoostClassifier
ada_clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth=1),
                    n_estimators=200,
                   learning_rate=0.5,
                   random_state=42                  
)

ada_clf.fit(X_train,y_train)
plot_decasion_boundary(ada_clf,X,y)

在这里插入图片描述

GBDT 梯度提升决策树

  • 将上一次做得不好的结果进行训练。
# 构造数据集
np.random.seed(42)
X = np.random.rand(100,1) - 0.5
y = 3*X[:,0]**2 + 0.05 * np.random.randn(100)

# 导入回归树
from sklearn.tree import DecisionTreeRegressor
tree_reg1 = DecisionTreeRegressor(max_depth=2)
tree_reg1.fit(X,y)

# 第一次预测的数据集误差作为下一次的训练集
y2 = y - tree_reg1.predict(X)

tree_reg2 = DecisionTreeRegressor(max_depth=2)
tree_reg2.fit(X,y2)

# 第二次预测的数据集误差作为下一次的训练集
y3 = y2 - tree_reg2.predict(X)

tree_reg3 = DecisionTreeRegressor(max_depth=2)
tree_reg3.fit(X,y3)

# 测试集
X_new = np.array([[0.8]])
# 用训练集测试每个模型之后,进行求和,即为最终预测值
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1,tree_reg2,tree_reg3))
y_pred
# array([0.75026781])

最终得到的预测值结果为0.75026781,是通过将每个模型的预测值进行求和得到的。

在sklearn中可以直接导入GBDT模型:

  1. GBDT
  2. XgBoost(建议)
  3. lightgbm(建议)

集成参数对比分析

  • 对上述三个树模型可视化展示
def plot_predictions(regressors, X, y, axes, label=None, style="r-", data_style="b.", data_label=None):
    x1 = np.linspace(axes[0], axes[1], 500) # 测试集
    y_pred = sum(regressor.predict(x1.reshape(-1, 1)) for regressor in regressors) # 预测值
    plt.plot(X[:, 0], y, data_style, label=data_label)
    plt.plot(x1, y_pred, style, linewidth=2, label=label)
    if label or data_label:
        plt.legend(loc="upper center", fontsize=16)
    plt.axis(axes)

plt.figure(figsize=(11,11))

plt.subplot(321)
plot_predictions([tree_reg1], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h_1(x_1)$", style="g-", data_label="Training set")
plt.ylabel("$y$", fontsize=16, rotation=0)
plt.title("Residuals and tree predictions", fontsize=16)

plt.subplot(322)
plot_predictions([tree_reg1], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h(x_1) = h_1(x_1)$", data_label="Training set")
plt.ylabel("$y$", fontsize=16, rotation=0)
plt.title("Ensemble predictions", fontsize=16)

plt.subplot(323)
plot_predictions([tree_reg2], X, y2, axes=[-0.5, 0.5, -0.5, 0.5], label="$h_2(x_1)$", style="g-", data_style="k+", data_label="Residuals")
plt.ylabel("$y - h_1(x_1)$", fontsize=16)

plt.subplot(324)
plot_predictions([tree_reg1, tree_reg2], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h(x_1) = h_1(x_1) + h_2(x_1)$")
plt.ylabel("$y$", fontsize=16, rotation=0)

plt.subplot(325)
plot_predictions([tree_reg3], X, y3, axes=[-0.5, 0.5, -0.5, 0.5], label="$h_3(x_1)$", style="g-", data_style="k+")
plt.ylabel("$y - h_1(x_1) - h_2(x_1)$", fontsize=16)
plt.xlabel("$x_1$", fontsize=16)

plt.subplot(326)
plot_predictions([tree_reg1, tree_reg2, tree_reg3], X, y, axes=[-0.5, 0.5, -0.1, 0.8], label="$h(x_1) = h_1(x_1) + h_2(x_1) + h_3(x_1)$")
plt.xlabel("$x_1$", fontsize=16)
plt.ylabel("$y$", fontsize=16, rotation=0)

plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bi2PNUn0-1663937761619)(../typora_image/image-20220922191604006.png)]

左列图中,表示残差的高低,越接近零,残差越小;反之,绿线向上或向下的幅度为残差较大。

右列图中,表示拟合的程度。

可以看出,在多次对模型进行集成后,可以降低残差、提高拟合的效果。

  • 用sklearn自带模型进行对比实验

建立三个模型,不同参数。

# 导入GBDT模型
from sklearn.ensemble import GradientBoostingRegressor

gbdt = GradientBoostingRegressor(max_depth=2, # 最大深度
                          n_estimators = 3, # 树的数量
                          learning_rate = 1.0 , # 相当于每次树的权重
                          random_state = 41
)
gbdt.fit(X,y)

gbdt_slow_1 = GradientBoostingRegressor(max_depth=2, 
                          n_estimators = 3, 
                          learning_rate = 0.1 , # 学习率变小
                          random_state = 41
)
gbdt_slow_1.fit(X,y)

gbdt_slow_2 = GradientBoostingRegressor(max_depth=2, 
                          n_estimators = 200, # 树的数量变多
                          learning_rate = 0.1 , 
                          random_state = 41
)
gbdt_slow_2.fit(X,y)
# 画图
plt.figure(figsize=(11,4))
plt.subplot(121)
plot_predictions([gbdt],X,y,axes=[-0.5,0.5,-0.1,0.8],label = 'Ensemble predictions')
plt.title('learning_rate={},n_estimators={}'.format(gbdt.learning_rate,gbdt.n_estimators))

plt.subplot(122)
plot_predictions([gbdt_slow_1],X,y,axes=[-0.5,0.5,-0.1,0.8],label = 'Ensemble predictions')
plt.title('learning_rate={},n_estimators={}'.format(gbdt_slow_1.learning_rate,gbdt_slow_1.n_estimators))

在这里插入图片描述

右图中,学习率变小了,但树的数量也小,其效果就很差。

所以,学习率变小时,n_estimators应设置较大(相当于迭代次数),所以有:

# 画图
plt.figure(figsize=(11,4))
plt.subplot(121)
plot_predictions([gbdt_slow_2],X,y,axes=[-0.5,0.5,-0.1,0.8],label = 'Ensemble predictions')
plt.title('learning_rate={},n_estimators={}'.format(gbdt_slow_2.learning_rate,gbdt_slow_2.n_estimators))

plt.subplot(122)
plot_predictions([gbdt_slow_1],X,y,axes=[-0.5,0.5,-0.1,0.8],label = 'Ensemble predictions')
plt.title('learning_rate={},n_estimators={}'.format(gbdt_slow_1.learning_rate,gbdt_slow_1.n_estimators))

在这里插入图片描述

增大了n_estimators参数,拟合效果变好了。

提前停止策略

  • 一般情况下,损失会根据迭代次数的增加而下降,然后到达一个平稳的阶段;但如果出现到达最小损失后进行了一个小幅度的增加,就要考虑在那个位置上停止算法的执行。
from sklearn.metrics import mean_squared_error
# 构造数据集
X_train,X_val,y_train,y_val = train_test_split(X,y,random_state=49)

gbdt = GradientBoostingRegressor(max_depth=2, 
                          n_estimators = 120, 
                          random_state = 42
)
gbdt.fit(X_train,y_train)

# 分阶段预测的均方误差值
errors = [mean_squared_error(y_val,y_pred) for y_pred in gbdt.staged_predict(X_val)]
# 找到最小值,即为最好情况
bst_n_estimators = np.argmin(errors) # 返回的是最小值下标

知道了最佳迭代次数,直接赋值给n_estimators,然后进行训练:

gbrt_best = GradientBoostingRegressor(max_depth=2, 
                          n_estimators = bst_n_estimators, 
                          random_state = 42
)
gbrt_best.fit(X_train,y_train)

# 最小损失值
min_error = np.min(errors)
min_error
# 0.003009529324545136
  • 画图
# 画图
plt.figure(figsize=(11,4))
plt.subplot(121)
# 损失值
plt.plot(errors,'b.-') 
plt.plot([bst_n_estimators,bst_n_estimators],[0,min_error],'k--') # 竖线
plt.plot([0,120],[min_error,min_error],'k--')  # 横线
plt.axis([0,120,0,0.01])
plt.title('Val Error')

# 最好的那次
plt.subplot(122)
plot_predictions([gbdt_best],X,y,axes=[-0.5,0.5,-0.1,0.8])
plt.title('Best Model(%d trees)'%bst_n_estimators)

左图中,虚线所对应位置为损失最小的地方,即最佳得带次数;

然后根据该次模型,得到右图最佳拟合效果,最佳的拟合参数为n_estimators=55。

  • 热启动,在前一阶段的训练结果上继续训练
gbrt = GradientBoostingRegressor(max_depth=2, 
                          random_state = 42,
                            warm_start = True # 热启动,在前一阶段的训练结果上继续训练
)
# 损失值上浮次数
error_going_up = 0 
# 初始化为无穷大
min_val_error = float('inf')

# n_estimator从1到120
for n_estimator in range(1,120):
    gbrt.n_estimators = n_estimator
    gbrt.fit(X_train,y_train)
    y_pred = gbrt.predict(X_val)
    val_error = mean_squared_error(y_val,y_pred)
    # 找到比之前小的损失值
    if val_error < min_val_error:
        min_val_error = val_error # 更新
        error_going_up = 0
    else:
        error_going_up += 1
    if error_going_up == 5: # 超过五次就停止
        break
            
print(gbrt.n_estimators) # 从55次开始,连续5次迭代都没有上升
# 61

Stacking(堆叠集成)

  • 第一阶段:选用多个模型进行训练。
  • 第二阶段:用上一阶段的结果作为训练集。
# 导入数据集,作10分类任务
from sklearn.datasets import fetch_mldata
from sklearn.model_selection import train_test_split

mnist = fetch_mldata('MNIST original')

# 分割数据集
X_train, X_val, y_train, y_val = train_test_split(
    mnist.data, mnist.target, test_size=10000, random_state=42)
# 导入多个分类器
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier
from sklearn.svm import LinearSVC
from sklearn.neural_network import MLPClassifier

# 第一阶段,分类器各自进行训练
random_forest_clf = RandomForestClassifier(random_state=42)
extra_trees_clf = ExtraTreesClassifier(random_state=42)
svm_clf = LinearSVC(random_state=42)
mlp_clf = MLPClassifier(random_state=42)

estimators = [random_forest_clf, extra_trees_clf, svm_clf, mlp_clf]

# 训练
for estimator in estimators:
    print('Training the',estimator)
    estimator.fit(X_train,y_train)
# 存放预测值
X_val_predictions = np.empty((len(X_val),len(estimators)),dtype=np.float32)

for index, estimator in enumerate(estimators):
    # 存储第一阶段的结果
    X_val_predictions[:,index] = estimator.predict(X_val)

# 得到四个分类器的预测结果
X_val_predictions
# 第二阶段,选择随机森林模型
rnd_forest_blender = RandomForestClassifier(n_estimators=200,oob_score=True,random_state=42)

rnd_forest_blender.fit(X_val_predictions,y_val)

# 验证集评估
rnd_forest_blender.oob_score_
# 0.9641
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值