机器学习实战刻意练习-- Task 4 Logistic回归

Week 2

Logistic回归(逻辑回归)

Logistic 回归或者叫逻辑回归,虽然名字有回归,但是它是用来做分类的。其主要思想是: 根据现有数据对分类边界线(Decision Boundary)建立回归公式,以此进行分类。这也是一种最优化方法。

Sigmoid 函数
  • 回归:假设现在有一些数据点,我们用一条直线对这些点进行拟合(这条直线称为最佳拟合直线),这个拟合的过程就叫做回归。从而可以得到对这些点的拟合直线方程。
  • 二值型输出分类函数:该函数称为海维塞得阶跃函数(Heaviside step function),或者直接称为单位阶跃函数。然而有一个问题, 该函数在跳跃点上从 0 瞬间跳跃到 1,这个瞬间跳跃过程有时难以处理。还好,有另一个函数也有类似的性质,且数学上更容易处理,这就是 Sigmoid 函数。
  • Sigmoid 函数具体的计算公式如下:
    sigmoid
  • 下图给出了 Sigmoid 函数在不同坐标尺度下的两条曲线图。当 x 为 0 时,Sigmoid 函数值为 0.5 。随着 x 的增大,对应的 Sigmoid 值将逼近于 1 ; 而随着 x 的减小, Sigmoid 值将逼近于 0 。如果横坐标刻度足够大, Sigmoid 函数看起来很像一个阶跃函数。
    ssssss
  • Logistic 回归也是一种概率估计。比如:这里Sigmoid 函数得出的值为0.5,可以理解为给定数据和参数,数据被分入 1类的概率为0.5。
基于最优化方法的回归系数确定

因此,为了实现 Logistic 回归分类器,我们可以在每个特征上都乘以一个回归系数,然后把所有结果值相加,将这个总和代入 Sigmoid 函数中,进而得到一个范围在 0~1 之间的数值,如下公式所示。Sigmoid 函数的输入记为 z 。
sdada
任何大于 0.5 的数据被分入 1 类,小于 0.5 即被归入 0 类。

梯度上升法

要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。如果梯度记为 ▽ ,则函数 f(x, y) 的梯度由下式表示:
sad
这个梯度意味着要沿 x 的方向移动 f(x, y)对x求偏导 ,沿 y 的方向移动 f(x, y)对y求偏导 。其中,函数f(x, y) 必须要在待计算的点上有定义并且可微。

梯度算子总是指向函数值增长最快的方向。这里所说的是移动方向,而未提到移动量的大小。该量值称为步长,记作 α 。用向量来表示的话,梯度上升算法的迭代公式如下:
梯度上升迭代公式
该公式将一直被迭代执行,直至达到某个停止条件为止,比如迭代次数达到某个指定值或者算法达到某个可以允许的误差范围。

局部最优现象 (Local Optima)

asfasdag
上图表示参数 θ 与误差函数 J(θ) 的关系图 (这里的误差函数是损失函数,所以我们要最小化损失函数),红色的部分是表示 J(θ) 有着比较高的取值,我们需要的是,能够让 J(θ) 的值尽量的低。也就是深蓝色的部分。θ0,θ1 表示 θ 向量的两个维度(此处的θ0,θ1是x0和x1的系数)。

可能梯度下降的最终点并非是全局最小点,可能是一个局部最小点,如我们上图中的右边的梯度下降曲线,描述的是最终到达一个局部最小点,这是我们重新选择了一个初始点得到的。

看来我们这个算法将会在很大的程度上被初始点的选择影响而陷入局部最小点。

Logistic 回归 原理

工作原理
每个回归系数初始化为 1
重复 R 次:
    计算整个数据集的梯度
    使用 步长 x 梯度 更新回归系数的向量
返回回归系数
开发流程
收集数据: 采用任意方法收集数据
准备数据: 由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳。
分析数据: 采用任意方法对数据进行分析。
训练算法: 大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
测试算法: 一旦训练步骤完成,分类将会很快。
使用算法: 首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。
算法特点
优点: 计算代价不高,易于理解和实现。
缺点: 容易欠拟合,分类精度可能不高。
适用数据类型: 数值型和标称型数据。

Logistic 回归 实战项目案例

项目案例1: 使用 Logistic 回归在简单数据集上的分类
项目概述

在一个简单的数据集上,采用梯度上升法找到 Logistic 回归分类器在此数据集上的最佳回归系数

开发流程

首先,需要创建一个logRegres.py文件,即创建logRegres模块,然后将下列相应的代码输入模块中。

# 1)收集数据: 可以使用任何方法
'''
我们采用存储在 TestSet.txt 文本文件中的数据,存储格式如下:

-0.017612	14.053064	0
-1.395634	4.662541	1
-0.752157	6.538620	0
-1.322371	7.152853	0
0.423363	11.054677	0
'''

# 2)准备数据: 由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳
import numpy as np
import matplotlib.pyplot as plt

# 使用 Logistic 回归在简单数据集上的分类

# 解析数据
def loadDataSet():
    '''
    Desc: 
        加载并解析数据
    Args:
        file_name -- 文件名称,要解析的文件所在磁盘位置
    Returns:
        dataMat -- 原始数据的特征
        labelMat -- 原始数据的标签,也就是每条样本对应的类别
    '''
    # dataMat为原始数据, labelMat为原始数据的标签
    dataMat = []
    labelMat = []
    fr = open('testSet.txt')
    for line in fr.readlines():
        lineArr = line.strip().split()
        if len(lineArr) == 1:
            continue    # 这里如果就一个空的元素,则跳过本次循环
        # 为了方便计算,我们将 X0 的值设为 1.0 ,也就是在每一行的开头添加一个 1.0 作为 X0
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
        labelMat.append(int(lineArr[2]))
    return dataMat, labelMat
        
# 3)分析数据: 采用任意方法对数据进行分析,此处不需要

# 4)训练算法: 使用梯度上升找到最佳参数
# 定义sigmoid阶跃函数
# sigmoid跳跃函数
def sigmoid(inX):
    return 1.0 / (1 + np.exp(-inX))

# Logistic 回归梯度上升优化算法    
# 正常的处理方案
# 两个参数:第一个参数==> dataMatIn 是一个2维NumPy数组,每列分别代表每个不同的特征,每行则代表每个训练样本。
# 第二个参数==> classLabels 是类别标签,它是一个 1*100 的行向量。为了便于矩阵计算,需要将该行向量转换为列向量,做法是将原向量转置,再将它赋值给labelMat。
def gradAscent(dataMatIn, classLabels):
    '''
    Desc:
        正常的梯度上升法
    Args:
        dataMatIn -- 输入的 数据的特征 List
        classLabels -- 输入的数据的类别标签
    Returns:
        array(weights) -- 得到的最佳回归系数
    '''

    # 转化为矩阵[[1,1,2],[1,1,2]....]
    dataMatrix = np.mat(dataMatIn)  # 转换为 NumPy 矩阵
    # 转化为矩阵[[0,1,0,1,0,1.....]],并转制[[0],[1],[0].....]
    # transpose() 行列转置函数
    # 将行向量转化为列向量   =>  矩阵的转置
    labelMat = np.mat(classLabels).transpose()  # 首先将数组转换为 NumPy 矩阵,然后再将行向量转置为列向量
    # m->数据量,样本数 n->特征数
    m, n = np.shape(dataMatrix)
    # print m, n, '__'*10, shape(dataMatrix.transpose()), '__'*100
    # alpha代表向目标移动的步长
    alpha = 0.001
    # 迭代次数
    maxCycles = 500
    # 生成一个长度和特征数相同的矩阵,此处n为3 -> [[1],[1],[1]]
    # weights 代表回归系数, 此处的 ones((n,1)) 创建一个长度和特征数相同的矩阵,其中的数全部都是 1
    weights = np.ones((n, 1))
    for k in range(maxCycles):  # heavy on matrix operations
        # m*3 的矩阵 * 3*1 的单位矩阵 = m*1的矩阵
        # 那么乘上单位矩阵的意义,就代表:通过公式得到的理论值
        # 参考地址: 矩阵乘法的本质是什么? https://www.zhihu.com/question/21351965/answer/31050145
        # print 'dataMatrix====', dataMatrix 
        # print 'weights====', weights
        # n*3   *  3*1  = n*1
        h = sigmoid(dataMatrix * weights)  # 矩阵乘法
        # print 'hhhhhhh====', h
        # labelMat是实际值
        error = (labelMat - h)  # 向量相减
        # 0.001* (3*m)*(m*1) 表示在每一个列上的一个误差情况,最后得出 x1,x2,xn的系数的偏移量
        weights = weights + alpha * dataMatrix.transpose() * error  # 矩阵乘法,最后得到回归系数
    return weights

# 运行代码
>>> import logRegres
>>> dataArr, labelMat = logRegres.loadDataSet('testSet.txt')
>>> logRegres.gradAscent(dataArr, labelMat)
matrix([[3.13830049],
        [0.31348991],
        [0.06173302]])
        
# 画出数据集和 Logistic 回归最佳拟合直线的函数
# 可视化展示
def plotBestFit(weights):
    '''
        Desc:
            将我们得到的数据可视化展示出来
        Args:
            dataArr:样本数据的特征
            labelMat:样本数据的类别标签,即目标变量
            weights:回归系数
        Returns:
            None
    '''
    dataMat, labelMat = loadDataSet()
    dataArr = np.array(dataMat)
    n = np.shape(dataArr)[0]
    xcord1 = []
    ycord1 = []
    xcord2 = []
    ycord2 = []
    for i in range(n):
        if int(labelMat[i]) == 1:
            xcord1.append(dataArr[i, 1])
            ycord1.append(dataArr[i, 2])
        else:
            xcord2.append(dataArr[i, 1])
            ycord2.append(dataArr[i, 2])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
    ax.scatter(xcord2, ycord2, s=30, c='green')
    x = np.arange(-3.0, 3.0, 0.1)
    """
    y的由来,卧槽,是不是没看懂?
    首先理论上是这个样子的。
    dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
    w0*x0+w1*x1+w2*x2=f(x)
    x0最开始就设置为1叻, x2就是我们画图的y值,而f(x)被我们磨合误差给算到w0,w1,w2身上去了
    所以: w0+w1*x+w2*y=0 => y = (-w0-w1*x)/w2   
    """
    y = (-weights[0] - weights[1] * x) / weights[2]
    ax.plot(x, y)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()

# 5)测试算法: 使用 Logistic 回归进行分类
def testLR():
    # 1.收集并准备数据
    dataMat, labelMat = loadDataSet("TestSet.txt")

    # print dataMat, '---\n', labelMat
    # 2.训练模型,  f(x)=a1*x1+b2*x2+..+nn*xn中 (a1,b2, .., nn).T的矩阵值
    # 因为数组没有是复制n份, array的乘法就是乘法
    dataArr = array(dataMat)
    # print dataArr
    weights = gradAscent(dataArr, labelMat)
    # weights = stocGradAscent0(dataArr, labelMat)
    # weights = stocGradAscent1(dataArr, labelMat)
    # print '*'*30, weights

    # 数据可视化
    plotBestFit(dataArr, labelMat, weights)

# 6)使用算法: 对简单数据集中数据进行分类
'''
梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理 100 个左右的数据集时尚可,
但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。
一种改进方法是一次仅用一个样本点来更新回归系数,该方法称为"随机梯度上升算法"。
'''
# 随机梯度上升算法可以写成如下的伪代码:
'''
所有回归系数初始化为 1
对数据集中每个样本
    计算该样本的梯度
    使用 alpha x gradient 更新回归系数值
返回回归系数值
'''
# 以下是随机梯度上升算法的实现代码:
# 随机梯度上升
# 梯度下降优化算法在每次更新数据集时都需要遍历整个数据集,计算复杂都较高
# 随机梯度上升一次只用一个样本点来更新回归系数
def stocGradAscent0(dataMatrix, classLabels):
    '''
    Desc:
        随机梯度上升,只使用一个样本点来更新回归系数
    Args:
        dataMatrix -- 输入数据的数据特征(除去最后一列)
        classLabels -- 输入数据的类别标签(最后一列数据)
    Returns:
        weights -- 得到的最佳回归系数
    '''
    m, n = np.shape(dataMatrix)
    alpha = 0.01
    # n*1的矩阵
    # 函数ones创建一个全1的数组
    weights = np.ones(n)  # 初始化长度为n的数组,元素全部为 1
    for i in range(m):
        # sum(dataMatrix[i]*weights)为了求 f(x)的值, f(x)=a1*x1+b2*x2+..+nn*xn,此处求出的 h 是一个具体的数值,而不是一个矩阵
        h = sigmoid(sum(dataMatrix[i] * weights))
        # print 'dataMatrix[i]===', dataMatrix[i]
        # 计算真实类别与预测类别之间的差值,然后按照该差值调整回归系数
        error = classLabels[i] - h
        # 0.01*(1*1)*(1*n)
        # print weights, "*" * 10, dataMatrix[i], "*" * 10, error
        weights = weights + alpha * error * dataMatrix[i]
    return weights

# 代码实现
>>> from numpy import *
>>> import importlib
>>> import logRegres
>>> importlib.reload(logRegres)
<module 'logRegres' from 'C:\\Users\\dell\\Desktop\\机器学习实战资料\\第五章 逻辑回归\\logRegres.py'>
>>> dataArr, labelMat = logRegres.loadDataSet()
>>> weights = logRegres.stocGradAscent0(array(dataArr), labelMat)
>>> logRegres.plotBestFit(weights)
>>> 
# 绘图如下:

图1

# 随机梯度上升算法(随机化)
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    '''
    Desc:
        改进版的随机梯度下降,使用随机的一个样本来更新回归系数
    Args:
        dataMatrix -- 输入数据的数据特征(除去最后一列数据)
        classLabels -- 输入数据的类别标签(最后一列数据)
        numIter=150 --  迭代次数
    Returns:
        weights -- 得到的最佳回归系数
    '''
    m, n = np.shape(dataMatrix)
    weights = np.ones(n)  # 创建与列数相同的矩阵的系数矩阵,所有的元素都是1
    # 随机梯度, 循环150,观察是否收敛
    for j in range(numIter):
        # [0, 1, 2 .. m-1]
        dataIndex = list(range(m))
        for i in range(m):
            # i和j的不断增大,导致alpha的值不断减少,但是不为0
            alpha = 4 / (1.0 + j + i) + 0.0001  # alpha 会随着迭代不断减小,但永远不会减小到0,因为后边还有一个常数项0.0001
            # 随机产生一个 0~len()之间的一个值
            # random.uniform(x, y) 方法将随机生成下一个实数,它在[x,y]范围内,x是这个范围内的最小值,y是这个范围内的最大值。
            randIndex = int(np.random.uniform(0, len(dataIndex)))
            # sum(dataMatrix[i]*weights)为了求 f(x)的值, f(x)=a1*x1+b2*x2+..+nn*xn
            h = sigmoid(sum(dataMatrix[dataIndex[randIndex]] * weights))
            error = classLabels[dataIndex[randIndex]] - h
            # print weights, '__h=%s' % h, '__'*20, alpha, '__'*20, error, '__'*20, dataMatrix[randIndex]
            weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]
            del (dataIndex[randIndex])
    return weights

# 代码实现
>>> from numpy import *
>>> import importlib
>>> import logRegres
>>> importlib.reload(logRegres)
<module 'logRegres' from 'C:\\Users\\dell\\Desktop\\机器学习实战资料\\第五章 逻辑回归\\logRegres.py'>
>>> dataArr, labelMat = logRegres.loadDataSet()
>>> weights = logRegres.stocGradAscent1(array(dataArr), labelMat)
>>> logRegres.plotBestFit(weights)
>>>

tu2

# 默认迭代次数是150,可以通过stocGradAscent()的第3个参数来对此进行修改。例子如下:
>>> weights = logRegres.stocGradAscent1(array(dataArr), labelMat, 500)
>>> logRegres.plotBestFit(weights)
>>> 
# 绘图如下:(相比直线网上平移了一点点)

图三

项目案例2: 从疝气病症预测病马的死亡率
项目概述:

使用 Logistic 回归来预测患有疝病的马的存活问题。疝病是描述马胃肠痛的术语。然而,这种病不一定源自马的胃肠问题,其他问题也可能引发马疝病。这个数据集中包含了医院检测马疝病的一些指标,有的指标比较主观,有的指标难以测量,例如马的疼痛级别。

开发流程:
# 1)收集数据: 给定数据文件
'''
病马的训练数据已经给出来了,如下形式存储在文本文件中:

1.000000	1.000000	39.200000	88.000000	20.000000	0.000000	0.000000	4.000000	1.000000	3.000000	4.000000	2.000000	0.000000	0.000000	0.000000	4.000000	2.000000	50.000000	85.000000	2.000000	2.000000	0.000000
2.000000	1.000000	38.300000	40.000000	24.000000	1.000000	1.000000	3.000000	1.000000	3.000000	3.000000	1.000000	0.000000	0.000000	0.000000	1.000000	1.000000	33.000000	6.700000	0.000000	0.000000	1.000000
'''

# 2)准备数据: 用 Python 解析文本文件并填充缺失值
'''
如何处理数据中的缺失值:
1)使用可用特征的均值来填补缺失值;
2)使用特殊值来填补缺失值,如 -1;
3)忽略有缺失值的样本;
4)使用有相似样本的均值添补缺失值;
5)使用另外的机器学习算法预测缺失值。
'''
# 现在,我们对下一节要用的数据集进行预处理,使其可以顺利地使用分类算法。在预处理需要做两件事:
'''
1)所有的缺失值必须用一个实数值来替换,因为我们使用的 NumPy 数据类型不允许包含缺失值。
我们这里选择实数 0 来替换所有缺失值,恰好能适用于 Logistic 回归。
这样做的直觉在于,我们需要的是一个在更新时不会影响系数的值。回归系数的更新公式如下:
weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]

如果 dataMatrix 的某个特征对应值为 0,那么该特征的系数将不做更新,即:
weights = weights

另外,由于 Sigmoid(0) = 0.5 ,即它对结果的预测不具有任何倾向性,因此我们上述做法也不会对误差造成任何影响。
基于上述原因,将缺失值用 0 代替既可以保留现有数据,也不需要对优化算法进行修改。
此外,该数据集中的特征取值一般不为 0,因此在某种意义上说它也满足 “特殊值” 这个要求。

2)如果在测试数据集中发现了一条数据的类别标签已经缺失,那么我们的简单做法是将该条数据丢弃。
这是因为类别标签与特征不同,很难确定采用某个合适的值来替换。采
用 Logistic 回归进行分类时这种做法是合理的,而如果采用类似 kNN 的方法,则保留该条数据显得更加合理。
'''
# 原始的数据集经过预处理后,保存成两个文件: horseColicTest.txt 和 horseColicTraining.txt 

# 3)分析数据: 可视化并观察数据
# 将数据使用 MatPlotlib 打印出来,观察数据是否是我们想要的格式

# 4)训练算法: 使用优化算法,找到最佳的系数
# 使用上个实例的原始的梯度上升算法,随机梯度上升算法,改进版随机梯度上升算法。下面就不一一写出代码了。

# 5)测试算法: 为了量化回归的效果,需要观察错误率。根据错误率决定是否回退到训练阶段,通过改变迭代的次数和步长的参数来得到更好的回归系数
# Logistic 回归分类函数
# 从疝气病症预测病马的死亡率
# 分类函数,根据回归系数和特征向量来计算 Sigmoid的值
def classifyVector(inX, weights):
    '''
    Desc: 
        最终的分类函数,根据回归系数和特征向量来计算 Sigmoid 的值,大于0.5函数返回1,否则返回0
    Args:
        inX -- 特征向量,features
        weights -- 根据梯度下降/随机梯度下降 计算得到的回归系数
    Returns:
        如果 prob 计算大于 0.5 函数返回 1
        否则返回 0
    '''
    prob = sigmoid(sum(inX * weights))
    if prob > 0.5:
        return 1.0
    else:
        return 0.0

# 打开测试集和训练集,并对数据进行格式化处理
def colicTest():
    '''
    Desc:
        打开测试集和训练集,并对数据进行格式化处理
    Args:
        None
    Returns:
        errorRate -- 分类错误率
    '''
    frTrain = open('horseColicTraining.txt')
    frTest = open('horseColicTest.txt')
    trainingSet = []
    trainingLabels = []
    # 解析训练数据集中的数据特征和Labels
    # trainingSet 中存储训练数据集的特征,trainingLabels 存储训练数据集的样本对应的分类标签
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[21]))
    # 使用 改进后的 随机梯度下降算法 求得在此数据集上的最佳回归系数 trainWeights
    trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500)
    # trainWeights = stocGradAscent0(array(trainingSet), trainingLabels)
    errorCount = 0
    numTestVec = 0.0
    # 读取 测试数据集 进行测试,计算分类错误的样本条数和最终的错误率
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(np.array(lineArr), trainWeights)) != int(currLine[21]):
            errorCount += 1
    errorRate = (float(errorCount) / numTestVec)
    print("the error rate of this test is: %f" % errorRate)
    return errorRate

# 调用 colicTest() 10次并求结果的平均值
def multiTest():
    numTests = 10
    errorSum = 0.0
    for k in range(numTests):
        errorSum += colicTest()
    print("after %d iterations the average error rate is: %f" % (numTests, errorSum / float(numTests)))

# 6)使用算法: 实现一个简单的命令行程序来收集马的症状并输出预测结果。
# 代码实现:
>>> import importlib
>>> import logRegres
>>> importlib.reload(logRegres)
<module 'logRegres' from 'C:\\Users\\dell\\Desktop\\机器学习实战资料\\第五章 逻辑回归\\logRegres.py'>
>>> logRegres.multiTest()

Warning (from warnings module):
  File "C:\Users\dell\Desktop\机器学习实战资料\第五章 逻辑回归\logRegres.py", line 32
    return 1.0 / (1 + np.exp(-inX))
RuntimeWarning: overflow encountered in exp
the error rate of this test is: 0.313433
the error rate of this test is: 0.343284
the error rate of this test is: 0.477612
the error rate of this test is: 0.388060
the error rate of this test is: 0.328358
the error rate of this test is: 0.417910
the error rate of this test is: 0.343284
the error rate of this test is: 0.268657
the error rate of this test is: 0.388060
the error rate of this test is: 0.432836
after 10 iterations the average error rate is: 0.370149
>>>

备注:

学习参考资料:
《机器学习实战》
https://github.com/apachecn/AiLearning/blob/master/docs/ml/5.Logistic%E5%9B%9E%E5%BD%92.md
https://blog.csdn.net/achuo/article/details/51160101

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值