机器学习——支持向量机

目录

一、支持向量机简介

1.1、硬间隔

1.2、软间隔

 1.3拉格朗日乘数法

1.4、核函数

二、实例 

2.1、数据处理

2.2、梯度下降法训练模型

2.3、预测正确率

2.4、显示正负类结果

2.5、绘图

 

 三、实验中遇到的问题

四、总结

优点

缺点

五、代码 

结果


一、支持向量机简介

支持向量机(Support Vector Machines, SVM)是一种监督学习模型,用于解决二分类问题,在机器学习领域有着广泛的应用。SVM通过在样本空间中寻找一个划分超平面,将不同类别的样本分开,同时使得两个点集到此平面的距离(即间隔)最大,因此又被称为最大间隔超平面。

1.1、硬间隔

硬间隔SVM试图找到一个能够完全分离数据的超平面。这适用于线性可分的数据集,但在实际应用中,数据往往是线性不可分的,这使得硬间隔SVM在实践中不太常用。硬间隔SVM的目标是最大化超平面的间隔(即离决策边界最近的样本到超平面的最小距离),同时保证所有样本都被正确分类。

具体地,硬间隔SVM的问题可以表述为:

[ \min_{\mathbf{w}, b} \frac{1}{2} |\mathbf{w}|^2 ] [ \text{subject to } y_i(\mathbf{w} \cdot \mathbf{x}_i + b) \geq 1, \quad \forall i ]

其中:

  • (\mathbf{w}) 是超平面的法向量
  • (b) 是超平面的偏置项
  • (y_i) 是第 (i) 个样本的标签(+1 或 -1)
  • (\mathbf{x}_i) 是第 (i) 个样本的特征向量

1.2、软间隔

软间隔SVM通过引入松弛变量来允许一定程度的分类错误,从而更适用于现实中的线性不可分数据。软间隔SVM在寻找超平面的过程中,不仅要最大化间隔,还要平衡分类错误的数量。

具体地,软间隔SVM的问题可以表述为:

[ \min_{\mathbf{w}, b, \xi} \frac{1}{2} |\mathbf{w}|^2 + C \sum_{i=1}^{n} \xi_i ] [ \text{subject to } y_i(\mathbf{w} \cdot \mathbf{x}_i + b) \geq 1 - \xi_i, \quad \xi_i \geq 0, \quad \forall i ]

其中:

  • (\xi_i) 是松弛变量,表示第 (i) 个样本允许的误差
  • (C) 是惩罚参数,控制间隔最大化和分类错误之间的权衡。较大的 (C) 值会减少分类错误但可能导致过拟合;较小的 (C) 值会增加分类错误但可能提升模型的泛化能力

 1.3拉格朗日乘数法

支持向量机(SVM)的优化问题通常使用拉格朗日乘数法进行求解。拉格朗日方法帮助我们将原始的优化问题转化为对偶问题,从而更容易求解。下面详细介绍硬间隔SVM和软间隔SVM的拉格朗日形式。

硬间隔SVM的拉格朗日形式

硬间隔SVM的目标是找到最大化间隔的超平面,同时保证所有样本都被正确分类。其优化问题为:

[ \min_{\mathbf{w}, b} \frac{1}{2} |\mathbf{w}|^2 ] [ \text{subject to } y_i(\mathbf{w} \cdot \mathbf{x}_i + b) \geq 1, \quad \forall i ]

为了将其转化为拉格朗日形式,我们引入拉格朗日乘数 (\alpha_i \geq 0),对应于每个约束条件(y_i(\mathbf{w} \cdot \mathbf{x}_i + b) - 1 \geq 0)。拉格朗日函数定义为:

[ L(\mathbf{w}, b, \boldsymbol{\alpha}) = \frac{1}{2} |\mathbf{w}|^2 - \sum_{i=1}^{n} \alpha_i [y_i(\mathbf{w} \cdot \mathbf{x}_i + b) - 1] ]

为了找到最优解,我们需要对 (\mathbf{w}) 和 (b) 求偏导并令其等于零:

[ \frac{\partial L}{\partial \mathbf{w}} = \mathbf{w} - \sum_{i=1}^{n} \alpha_i y_i \mathbf{x}i = 0 ] [ \mathbf{w} = \sum{i=1}^{n} \alpha_i y_i \mathbf{x}_i ]

[ \frac{\partial L}{\partial b} = -\sum_{i=1}^{n} \alpha_i y_i = 0 ]

将这些条件代入拉格朗日函数,得到对偶问题:

[ \max_{\boldsymbol{\alpha}} \sum_{i=1}^{n} \alpha_i - \frac{1}{2} \sum_{i=1}^{n} \sum_{j=1}^{n} \alpha_i \alpha_j y_i y_j (\mathbf{x}_i \cdot \mathbf{x}j) ] [ \text{subject to } \sum{i=1}^{n} \alpha_i y_i = 0 ] [ \alpha_i \geq 0, \quad \forall i ]

软间隔SVM的拉格朗日形式

软间隔SVM允许分类错误,并引入松弛变量 (\xi_i)。其优化问题为:

[ \min_{\mathbf{w}, b, \boldsymbol{\xi}} \frac{1}{2} |\mathbf{w}|^2 + C \sum_{i=1}^{n} \xi_i ] [ \text{subject to } y_i(\mathbf{w} \cdot \mathbf{x}_i + b) \geq 1 - \xi_i, \quad \xi_i \geq 0, \quad \forall i ]

引入拉格朗日乘数 (\alpha_i \geq 0)(\mu_i \geq 0) 对应于每个约束条件 (y_i(\mathbf{w} \cdot \mathbf{x}_i + b) - 1 + \xi_i \geq 0) (\xi_i \geq 0)。拉格朗日函数为:

[ L(\mathbf{w}, b, \boldsymbol{\xi}, \boldsymbol{\alpha}, \boldsymbol{\mu}) = \frac{1}{2} |\mathbf{w}|^2 + C \sum_{i=1}^{n} \xi_i - \sum_{i=1}^{n} \alpha_i [y_i(\mathbf{w} \cdot \mathbf{x}i + b) - 1 + \xi_i] - \sum{i=1}^{n} \mu_i \xi_i ]

同样,对 (\mathbf{w}), (b), 和(\boldsymbol{\xi}) 求偏导并令其等于零:

[ \frac{\partial L}{\partial \mathbf{w}} = \mathbf{w} - \sum_{i=1}^{n} \alpha_i y_i \mathbf{x}i = 0 ] [ \mathbf{w} = \sum{i=1}^{n} \alpha_i y_i \mathbf{x}_i ]
[ \frac{\partial L}{\partial b} = -\sum_{i=1}^{n} \alpha_i y_i = 0 ] [ \frac{\partial L}{\partial \xi_i} = C - \alpha_i - \mu_i = 0 ] [ \alpha_i = C - \mu_i ]

将这些条件带入拉格朗日函数中,得到软间隔SVM的对偶问题:

[ \max_{\boldsymbol{\alpha}} \sum_{i=1}^{n} \alpha_i - \frac{1}{2} \sum_{i=1}^{n} \sum_{j=1}^{n} \alpha_i \alpha_j y_i y_j (\mathbf{x}_i \cdot \mathbf{x}j) ] [ \text{subject to } \sum{i=1}^{n} \alpha_i y_i = 0 ] [ 0 \leq \alpha_i \leq C, \quad \forall i ]

在实际应用中,我们通常通过数值优化方法(如SMO算法)来求解这个对偶问题,从而找到支持向量和决策边界。

1.4、核函数

核函数在支持向量机(SVM)中扮演着重要的角色,它可以将输入空间中的数据映射到高维特征空间,从而使非线性可分的问题在高维空间中变得线性可分。这样可以通过线性分类器(如硬间隔SVM)来处理非线性分类问题。常见的核函数包括线性核、多项式核、高斯核等。

线性核

线性核是最简单的核函数,它直接在原始输入空间中进行线性分类。线性核的形式为:

[ K(\mathbf{x}_i, \mathbf{x}_j) = \mathbf{x}_i \cdot \mathbf{x}_j ]

多项式核

多项式核引入了多项式的非线性映射,可以处理一定程度上的非线性分类问题。多项式核的形式为:

[ K(\mathbf{x}_i, \mathbf{x}_j) = (\gamma \mathbf{x}_i \cdot \mathbf{x}_j + r)^d ]

其中,(\gamma)是尺度因子,(r) 是常数项,(d) 是多项式的次数。

二、实例 

我的代码是利用支持向量机实现鸢尾花的二分类

2.1、数据处理

利用库中的鸢尾花数据,对鸢尾花中的数据分为训练集与测试集并进行标准化处理

# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data  # 特征数据
y = iris.target  # 标签数据

# 只取两类数据进行二分类
X = X[y != 2]  # 去掉第三类
y = y[y != 2]  # 去掉第三类的标签
y = np.where(y == 0, -1, 1)  # 将类别标签0改为-1

# 数据集划分为训练集和测试集
np.random.seed(42)  # 设置随机种子
shuffle_indices = np.random.permutation(len(X))  # 打乱数据
X, y = X[shuffle_indices], y[shuffle_indices]  # 重新排序数据
split_index = int(0.7 * len(X))  # 计算70%数据的索引
X_train, X_test = X[:split_index], X[split_index:]  # 分割为训练集
y_train, y_test = y[:split_index], y[split_index:]  # 分割为测试集

# 标准化特征
mean = X_train.mean(axis=0)  # 计算训练集均值
std = X_train.std(axis=0)  # 计算训练集标准差
X_train = (X_train - mean) / std  # 标准化训练集
X_test = (X_test - mean) / std  # 标准化测试集

2.2、梯度下降法训练模型

首先初始化权重w和偏置b,然后进行迭代更新。在每次迭代中,遍历训练数据集,检查是否满足条件,如果满足条件,则更新权重;否则,同时更新权重和偏置。

# SVM参数
learning_rate = 0.001  # 学习率
lambda_param = 0.01  # 正则化参数
C = 1.0  # 惩罚参数
num_iterations = 1000  # 迭代次数

# 初始化权重和偏置
w = np.zeros(X_train.shape[1])  # 初始化权重为零
b = 0  # 初始化偏置为零

# 梯度下降法训练模型
for i in range(num_iterations):  # 迭代500次
    for idx, x_i in enumerate(X_train):
        condition = y_train[idx] * (np.dot(x_i, w) + b) >= 1  # 检查是否满足条件
        if condition:
            w -= learning_rate * (2 * lambda_param * w)  # 更新权重
        else:
            w -= learning_rate * (2 * lambda_param * w - np.dot(x_i, y_train[idx]) * C)  # 更新权重
            b -= learning_rate * y_train[idx] * C  # 更新偏置

2.3、预测正确率

def predict(X):
    return np.sign(np.dot(X, w) + b)  # 计算预测值

# 测试模型
y_pred = predict(X_test)  # 预测测试集

# 计算准确率
accuracy = np.mean(y_pred == y_test)  # 计算准确率
print(f'Accuracy: {accuracy:.2f}')

 

2.4、显示正负类结果

函数接受两个参数:y_true表示真实标签,y_pred表示预测标签。函数首先计算真正例(TP)、真负例(TN)、假正例(FP)和假负例(FN),然后分别计算正类和负类的精确度、召回率和F1分数,并将结果打印出来。

def classification_report(y_true, y_pred):
    tp = np.sum((y_true == 1) & (y_pred == 1))  # 真正例
    tn = np.sum((y_true == -1) & (y_pred == -1))  # 真负例
    fp = np.sum((y_true == -1) & (y_pred == 1))  # 假正例
    fn = np.sum((y_true == 1) & (y_pred == -1))  # 假负例

    precision_pos = tp / (tp + fp) if tp + fp > 0 else 0  # 计算正类精度
    recall_pos = tp / (tp + fn) if tp + fn > 0 else 0  # 计算正类召回率
    f1_pos = 2 * (precision_pos * recall_pos) / (precision_pos + recall_pos) if precision_pos + recall_pos > 0 else 0  # 计算正类F1

    precision_neg = tn / (tn + fn) if tn + fn > 0 else 0  # 计算负类精度
    recall_neg = tn / (tn + fp) if tn + fp > 0 else 0  # 计算负类召回率
    f1_neg = 2 * (precision_neg * recall_neg) / (precision_neg + recall_neg) if precision_neg + recall_neg > 0 else 0  # 计算负类F1

    print(f"Class -1: Precision: {precision_neg:.2f}, Recall: {recall_neg:.2f}, F1-score: {f1_neg:.2f}")  # 打印负类报告
    print(f"Class  1: Precision: {precision_pos:.2f}, Recall: {recall_pos:.2f}, F1-score: {f1_pos:.2f}")  # 打印正类报告

2.5、绘图

def plot_decision_boundary(X, y, w, b):
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1  # 设置x轴范围
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1  # 设置y轴范围
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, 0.02), np.arange(x2_min, x2_max, 0.02))  # 创建网格
    grid = np.c_[xx1.ravel(), xx2.ravel()]  # 网格点
    Z = predict_2d((grid - mean_2d) / std_2d)  # 使用前两个特征的均值和标准差进行标准化
    Z = Z.reshape(xx1.shape)  # 预测值形状调整

    plt.contourf(xx1, xx2, Z, colors=['lightgray', 'lightyellow'], alpha=0.3)  # 绘制等高线

    # 绘制支持向量
    distance_from_hyperplane = np.abs(np.dot(X, w) + b - 1)  # 距离超平面距离
    sv = distance_from_hyperplane < 0.1  # 找到支持向量
    plt.scatter(X[sv, 0], X[sv, 1], s=200, facecolors='none', edgecolors='k')  # 绘制支持向量

    # 绘制所有样本
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired, edgecolors='k')  # 绘制样本点

    plt.xlabel('Feature 1')  # x轴标签
    plt.ylabel('Feature 2')  # y轴标签
    plt.title('SVM Decision Boundary and Support Vectors')  # 图标题
    plt.show()

 

 三、实验中遇到的问题

在实验过程中改变了训练模型的训练次数后,他的超平面会发生很大的改变,这是迭代次数为1000次时的图像:

而这是迭代次数为500次的图像:

四、总结

优点

  1. 有效处理高维数据:SVM在高维空间中表现优异,能够处理特征维数大于样本数的情况。它的性能不会因为特征数量多而显著下降。

  2. 支持非线性分类:通过核函数,SVM可以将原始数据映射到高维空间,从而能够有效地处理非线性分类问题。常用的核函数如线性核、多项式核、高斯核等。

  3. 健壮性强:SVM对少量的噪声和异常值具有一定的鲁棒性,尤其是软间隔SVM,它通过引入松弛变量来允许一定程度的误分类。

  4. 理论基础扎实:SVM基于统计学习理论中的结构风险最小化原则,其目标是找到一个能够最大化分类间隔的超平面,从而在理论上能保证较好的泛化能力。

  5. 唯一解:由于SVM的优化问题是一个凸优化问题,其解是全局最优且唯一的,这避免了像神经网络那样可能陷入局部最优的问题。

缺点

  1. 计算复杂度高:对于大规模数据集,SVM的训练时间和内存需求会急剧增加,特别是在使用非线性核函数时。这使得SVM在处理大规模数据时效率不高。

  2. 参数选择复杂:SVM模型有多个超参数(如惩罚参数 (C) 和核函数参数 (\sigma) 等),这些参数的选择对模型性能影响很大,通常需要通过交叉验证等方法进行调优。

  3. 对缺失数据敏感:SVM对缺失数据较为敏感,需要在训练前对数据进行预处理,确保数据完整性。

  4. 解释性差:与决策树等模型相比,SVM模型的可解释性较差,难以提供直观的规则或逻辑来解释分类结果。

  5. 不能直接输出概率:SVM本质上是一个二分类器,虽然可以通过一些后处理方法(如Platt缩放)来输出分类概率,但这些方法增加了额外的复杂度。

五、代码 

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data  # 特征数据
y = iris.target  # 标签数据

# 只取两类数据进行二分类
X = X[y != 2]  # 去掉第三类
y = y[y != 2]  # 去掉第三类的标签
y = np.where(y == 0, -1, 1)  # 将类别标签0改为-1

# 数据集划分为训练集和测试集
np.random.seed(42)  # 设置随机种子
shuffle_indices = np.random.permutation(len(X))  # 打乱数据
X, y = X[shuffle_indices], y[shuffle_indices]  # 重新排序数据
split_index = int(0.7 * len(X))  # 计算70%数据的索引
X_train, X_test = X[:split_index], X[split_index:]  # 分割为训练集
y_train, y_test = y[:split_index], y[split_index:]  # 分割为测试集

# 标准化特征
mean = X_train.mean(axis=0)  # 计算训练集均值
std = X_train.std(axis=0)  # 计算训练集标准差
X_train = (X_train - mean) / std  # 标准化训练集
X_test = (X_test - mean) / std  # 标准化测试集

# SVM参数
learning_rate = 0.001  # 学习率
lambda_param = 0.01  # 正则化参数
C = 1.0  # 惩罚参数
num_iterations = 500  # 迭代次数

# 初始化权重和偏置
w = np.zeros(X_train.shape[1])  # 初始化权重为零
b = 0  # 初始化偏置为零

# 梯度下降法训练模型
for i in range(num_iterations):  # 迭代500次
    for idx, x_i in enumerate(X_train):
        condition = y_train[idx] * (np.dot(x_i, w) + b) >= 1  # 检查是否满足条件
        if condition:
            w -= learning_rate * (2 * lambda_param * w)  # 更新权重
        else:
            w -= learning_rate * (2 * lambda_param * w - np.dot(x_i, y_train[idx]) * C)  # 更新权重
            b -= learning_rate * y_train[idx] * C  # 更新偏置

# 预测函数
def predict(X):
    return np.sign(np.dot(X, w) + b)  # 计算预测值

# 测试模型
y_pred = predict(X_test)  # 预测测试集

# 计算准确率
accuracy = np.mean(y_pred == y_test)  # 计算准确率
print(f'Accuracy: {accuracy:.2f}')

# 打印分类报告
def classification_report(y_true, y_pred):
    tp = np.sum((y_true == 1) & (y_pred == 1))  # 真正例
    tn = np.sum((y_true == -1) & (y_pred == -1))  # 真负例
    fp = np.sum((y_true == -1) & (y_pred == 1))  # 假正例
    fn = np.sum((y_true == 1) & (y_pred == -1))  # 假负例

    precision_pos = tp / (tp + fp) if tp + fp > 0 else 0  # 计算正类精度
    recall_pos = tp / (tp + fn) if tp + fn > 0 else 0  # 计算正类召回率
    f1_pos = 2 * (precision_pos * recall_pos) / (precision_pos + recall_pos) if precision_pos + recall_pos > 0 else 0  # 计算正类F1

    precision_neg = tn / (tn + fn) if tn + fn > 0 else 0  # 计算负类精度
    recall_neg = tn / (tn + fp) if tn + fp > 0 else 0  # 计算负类召回率
    f1_neg = 2 * (precision_neg * recall_neg) / (precision_neg + recall_neg) if precision_neg + recall_neg > 0 else 0  # 计算负类F1

    print(f"Class -1: Precision: {precision_neg:.2f}, Recall: {recall_neg:.2f}, F1-score: {f1_neg:.2f}")  # 打印负类报告
    print(f"Class  1: Precision: {precision_pos:.2f}, Recall: {recall_pos:.2f}, F1-score: {f1_pos:.2f}")  # 打印正类报告

classification_report(y_test, y_pred)

# 打印混淆矩阵
def confusion_matrix(y_true, y_pred):
    tp = np.sum((y_true == 1) & (y_pred == 1))  # 真正例
    tn = np.sum((y_true == -1) & (y_pred == -1))  # 真负例
    fp = np.sum((y_true == -1) & (y_pred == 1))  # 假正例
    fn = np.sum((y_true == 1) & (y_pred == -1))  # 假负例
    return np.array([[tn, fp],
                     [fn, tp]])  # 返回混淆矩阵

print('Confusion Matrix:')
print(confusion_matrix(y_test, y_pred))

# 使用前两个特征进行绘制
X_train_2d = X_train[:, :2]  # 取前两个特征
w_2d = w[:2]  # 取前两个特征的权重

# 标准化前两个特征的均值和标准差
mean_2d = mean[:2]  # 前两个特征的均值
std_2d = std[:2]  # 前两个特征的标准差

# 定义用于预测的函数
def predict_2d(X):
    return np.sign(np.dot(X, w_2d) + b)  # 计算二维特征的预测值

# 绘制决策边界和支持向量
def plot_decision_boundary(X, y, w, b):
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1  # 设置x轴范围
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1  # 设置y轴范围
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, 0.02), np.arange(x2_min, x2_max, 0.02))  # 创建网格
    grid = np.c_[xx1.ravel(), xx2.ravel()]  # 网格点
    Z = predict_2d((grid - mean_2d) / std_2d)  # 使用前两个特征的均值和标准差进行标准化
    Z = Z.reshape(xx1.shape)  # 预测值形状调整

    plt.contourf(xx1, xx2, Z, colors=['lightgray', 'lightyellow'], alpha=0.3)  # 绘制等高线

    # 绘制支持向量
    distance_from_hyperplane = np.abs(np.dot(X, w) + b - 1)  # 距离超平面距离
    sv = distance_from_hyperplane < 0.1  # 找到支持向量
    plt.scatter(X[sv, 0], X[sv, 1], s=200, facecolors='none', edgecolors='k')  # 绘制支持向量

    # 绘制所有样本
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired, edgecolors='k')  # 绘制样本点

    plt.xlabel('Feature 1')  # x轴标签
    plt.ylabel('Feature 2')  # y轴标签
    plt.title('SVM Decision Boundary and Support Vectors')  # 图标题
    plt.show()

# 使用前两个特征调用绘制函数
plot_decision_boundary(X_train_2d, y_train, w_2d, b)

结果

 

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
支持向量机(Support Vector Machine, SVM)的算法步骤如下: 1. 收集训练样本集,并将每个样本表示为特征向量。每个特征向量可以是M维的向量,其中M为特征的数量。 2. 选择一个合适的核函数或非线性变换方式,将原始特征空间映射到一个更高维的特征空间。这个变换可以使线性不可分的样本集变为线性可分。 3. 基于训练样本集,建立一个支持向量机模型。该模型的目标是找到一个超平面,能够最大化样本间的间隔并且正确地分类样本。间隔是指支持向量与超平面之间的距离。 4. 通过求解优化问题,确定超平面的参数。优化问题的目标是最小化模型的结构风险,同时最大化间隔。这可以通过使用拉格朗日乘子法和KKT条件来实现。 5. 根据优化问题的解,确定支持向量,即位于间隔边界上的样本。这些样本对于模型的构建和分类起到关键作用。 6. 对于新的未知样本,使用训练好的模型进行分类。通过计算样本到超平面的距离,确定样本属于哪个类别。 总结起来,支持向量机算法步骤包括收集训练样本集、选择合适的核函数或非线性变换方式、建立模型、确定超平面的参数、确定支持向量,最后使用模型进行分类。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [机器学习——支持向量机](https://blog.csdn.net/weixin_55252589/article/details/122688871)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值