机器学习 —— Logistic回归

目录

一、回归的基础概念

二、基于Logistic回归和Sigmoid函数的分类

三、基于最优化方法的最佳回归系数确定

1、极大似然估计 Recap

2、梯度上升法

3、准备数据:一个简单的数据集

4、训练算法:使用梯度上升找到最佳参数

5、分析数据:画出决策边界

6、训练算法:随机梯度上升算法

7、改进的随机梯度上升算法

四、实例:从疝气病症状预测病马的死亡率

1、准备数据:处理数据中的缺失值

2、测试算法:使用梯度上升算法进行分类

3、测试算法:使用改进后的随机梯度上升算法进行分类

4、实例:实践小结


一、回归的基础概念

        现有一些数据点,我们用 一条直线对这些点进行拟合,该线称为最佳拟合直线,这个拟合过程就称作回归。利用Logistic 回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类。这里的 “回归”一词源于最佳拟合,表示要找到最佳拟合参数集。 训练分类器时的做法就是寻找最佳拟合参数,使用的是最优化算法。

回归
Logistic回归的一般过程

(1) 收集数据:采用任意方法收集数据。

(2) 准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳。

(3) 分析数据:采用任意方法对数据进行分析。

(4) 训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。

(5) 测试算法:一旦训练步骤完成,分类将会很快。

(6) 使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值; 接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。

二、基于Logistic回归和Sigmoid函数的分类

Logistic回归
  • 优点:计算代价不高,易于理解和实现。
  • 缺点:容易欠拟合,分类精度可能不高。
  • 适用数据类型:数值型和标称型数据。

        Logistic回归来做分类问题,我们想要的函数应该是,能接受所有的输入然后预测出类别。例如,在两个类的情况下,上述函数输出0或1。例如海维塞德阶跃函数 (Heaviside step function),也称为单位阶跃函数。

        海维塞德阶跃函数的问题在于: 该函数在跳跃点上从0瞬间跳跃到1(不连续、不可微),这个瞬间跳跃过程有时很难处理。但是在数学上,Sigmoid函数可以可以解决这个问题。Sigmoid函数具体的计算公式如下:

        下图给出了Sigmoid函数在不同坐标尺度下的两条曲线图。当x为0时,Sigmoid函数值为0.5。 随着x的增大,对应的Sigmoid值将逼近于1;而随着x的减小,Sigmoid值将逼近于0。如果横坐标 刻度足够大,Sigmoid函数看起来很像一个阶跃函数。

        所以,为了实现Logistic回归分类,我们可以在每个特征上都乘以一个回归系数,然后把 所有的结果值相加,将这个总和代入Sigmoid函数中,进而得到一个范围在0~1之间的数值。任何大于0.5的数据被分入1类,小于0.5即被归入0类。所以,Logistic回归也可以被看成是一种概率估计。

预测值与输出标记:  
其中Sigmoid函数的输入值为z、向量x是分类器的输入数据,向量w是找到的最佳系数,b为常数。

运用Sigmoid函数

 对数几率(log odds):样本作为正例的相对可能性的对数 

 因此有:

上面两个式子分别表示y=1和y=0的概率。通过,我们获得z值,再通过Sigmoid函数把z值映射到0~1之间,获得数值之后就可进行分类。比如定义,大于0.5的分类为1,反之,即分类为0。所以我们要解决的问题就是获得最佳回归系数,即求解w和b得值。

三、基于最优化方法的最佳回归系数确定

1、极大似然估计 Recap

① 极大似然估计的方法步骤

  • 确定待求解的未知参数,如均值、方差或特定分布函数的参数等;
  • 计算每个样本的概率密度为
  • 根据样本的概率密度累乘构造似然函数:
  • 通过似然函数最大化(求导为0),求解未知参数θ,为了降低计算难度,可采用对数加法替换概率乘法,通过导数为0/极大值来求解未知参数。

②最大化样本属于其真实标记的概率,等同于最大化对数似然函数:

③转化为最大化逻辑斯蒂似然函数求解:



④根据sigmoid函数,似然函数可重写为:

 故等价形式为要最小化该方程。

2、梯度上升法

         梯度上升法基于的思想是:要找到某函数的 最大值,最好的方法是沿着该函数的梯度方向探寻。如果梯度记为∇,则函数f(x,y)的梯度由 下式表示:

这个梯度意味着要沿x的方向移动 ,沿y的方向移动 。其中,函数f(x,y) 必须要在待计算的点上有定义并且可微。如下图:

        如图,梯度上升算法到达每个点后都会重新估计移动的方向。从P0开始,计算完该点的梯度,函数就根据梯度移动到下一点P1。在P1点,梯度再次被重新计算,并沿新的梯度方向移动到P2。如此循环迭代,直到满足停止条件。迭代的过程中,梯度算子总是保证我们能选取到最佳的移动方向。

        梯度上升算法沿梯度方向移动了一步。可以看到,梯度算子总是指向函数值增长最快的方向。这里所说的是移动方向,而未提到移动量的大小。该量值称为步长,记做α。用向 量来表示的话,梯度上升算法的迭代公式如下:

        该公式将一直被迭代执行,直至达到某个停止条件为止,比如迭代次数达到某个指定值或算 法达到某个可以允许的误差范围。

梯度下降算法

梯度下降算法,它与这里的梯度上升算法是一样的,只是公式中的加法需要变成减法。因此,对应的公式可以写成:

 梯度上升算法用来求函数的最大值,而梯度下降算法用来求函数的最小值。

3、准备数据:一个简单的数据集

testSet.txt的数据
X1X2分类标签
0.35571510.3259760
-1.395634.6625411
-0.752166.538620
-1.322377.1528530
 ......············
1.9852983.2306191
-1.69345-0.557541
-0.5765311.778920
-0.34681-1.678731
-2.124482.6724711

        有100个样本点,每个点包含两个数值型特征:X1和X2。在此数据集上,通过使用梯度上升法找到最佳回归系数,也就是拟合出Logistic回归模型的最佳参数。

# 加载数据集
def loadDataSet():
    dataMat = []                                                      # 数据列表
    labelMat = []                                                     # 标签列表
    fr = open('testSet.txt')                                          # 打开文件
    for line in fr.readlines():                                       # 遍历每行,读取数据
        lineArr = line.strip().split()                                # 去除回车符
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])   # 把此行的数据添加到数据列表里面
        labelMat.append(int(lineArr[2]))                              # 添加标签进列表
    return dataMat, labelMat
# 绘制数据集
def showData():
    dataMat, labelMat = loadDataSet()                      # 加载数据集,标签
    dataArr = array(dataMat)                               # 转换成umPy的数组
    n = shape(dataArr)[0]                                  # 获取数据总数
    xcord1 = []; ycord1 = []                               # 存放正样本
    xcord2 = []; ycord2 = []                               # 存放负样本
    for i in range(n):                                     # 依据数据集的标签来对数据进行分类
        if int(labelMat[i]) == 1:                          # 数据的标签为1,表示为正样本
            xcord1.append(dataArr[i, 1]); ycord1.append(dataArr[i, 2])
        else:                                              # 否则,若数据的标签不为1,表示为负样本
            xcord2.append(dataArr[i, 1]); ycord2.append(dataArr[i, 2])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcord1, ycord1, s=15, c='blue')             # 绘制正样本
    ax.scatter(xcord2, ycord2, s=15, c='red', marker='s')  # 绘制负样本
    plt.title('DateSet')                                   # 标题
    plt.xlabel('X1'); plt.ylabel('X2')                     # x,y轴的标签
    plt.show()

4、训练算法:使用梯度上升找到最佳参数

     

# sigmoid函数
def sigmoid(inX):
    return 1.0 / (1 + exp(-inX))
# 梯度上升算法
def gradAscent(dataMatIn, classLabels):                            # dataMatIn数据集、classLabels数据标签
    dataMatrix = mat(dataMatIn)                                    # 转换为NumPy矩阵
    labelMat = mat(classLabels).transpose()                        # 转换为NumPy矩阵,并且矩阵转置
    m, n = shape(dataMatrix)                                       # 获取数据集矩阵的大小,m为行数,n为列数
    alpha = 0.001                                                  # 目标移动的步长
    maxCycles = 500                                                # 迭代次数
    weights = ones((n, 1))                                         # 权重初始化为1
    for k in range(maxCycles):                                     # 重复矩阵运算
        h = sigmoid(dataMatrix * weights)                          # 矩阵相乘,计算sigmoid函数
        error = (labelMat - h)                                     # 计算误差
        weights = weights + alpha * dataMatrix.transpose() * error # 矩阵相乘,更新权重
    return weights
# 获得回归系数
dataMat, labelMat = loadDataSet()
weigths = gradAscent(dataMat, labelMat)
print("w0: %f, w1: %f, W2: %f" % (weigths[0], weigths[1], weigths[2]))

  

5、分析数据:画出决策边界

         上面已经解出了一组回归系数,它确定了不同类别数据之间的分隔线。现在我们可以尝试去画出这个分隔线,

# 绘制数据集和Logistic回归最佳拟合直线
def plotBestFit(weights):
    dataMat, labelMat = loadDataSet()                      # 加载数据集,标签
    dataArr = array(dataMat)                               # 转换成umPy的数组
    n = shape(dataArr)[0]                                  # 获取数据总数
    xcord1 = []; ycord1 = []                               # 存放正样本
    xcord2 = []; ycord2 = []                               # 存放负样本
    for i in range(n):                                     # 依据数据集的标签来对数据进行分类
        if int(labelMat[i]) == 1:                          # 数据的标签为1,表示为正样本
            xcord1.append(dataArr[i, 1]); ycord1.append(dataArr[i, 2])
        else:                                              # 否则,若数据的标签不为1,表示为负样本
            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 = arange(-3.0, 3.0, 0.1)                             # x区间
    y = (-weights[0] - weights[1] * x) / weights[2]        # 最佳拟合直线
    ax.plot(x, y)
    plt.title('BestFit')                                   # 标题
    plt.xlabel('X1'); plt.ylabel('X2')                     # x,y轴的标签
    plt.show()
# 运行画出图
dataMat, labelMat = loadDataSet()
weigths = gradAscent(dataMat, labelMat)
plotBestFit(weigths.getA())
print("w0: %f, w1: %f, W2: %f" % (weigths[0], weigths[1], weigths[2]))

         从得出的结果图,我们可以看出,这个分类结果很不错了,从图上看出错分点的点不多。但是,尽管例子简单且数据集很小, 这个方法却需要300次乘法大量的计算。

6、训练算法:随机梯度上升算法

        梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理100个左右的数 据集时尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。一种 改进方法是一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法。由于可以在新 样本到来时对分类器进行增量式更新,因而随机梯度上升算法是一个在线学习算法。与“在线学习”相对应,一次处理所有数据被称作是“批处理”。

# 随机梯度上升算法
def stocGradAscent0(dataMatrix, classLabels):              # dataMatIn数据集、classLabels数据标签
    m, n = shape(dataMatrix)                               # 获取数据集矩阵的大小,m为行数,n为列数
    alpha = 0.01                                           # 目标移动的步长
    weights = ones(n)                                      # 所以初始化为1
    for i in range(m):                                     # 重复矩阵运算
        h = sigmoid(sum(dataMatrix[i] * weights))          # 矩阵相乘,计算sigmoid函数
        error = classLabels[i] - h                         # 计算误差
        weights = weights + alpha * error * dataMatrix[i]  # 矩阵相乘,更新权重
    return weights

        可以看到,随机梯度上升算法与梯度上升算法在代码上很相似,但也有一些区别:第一,后 者的变量h和误差error都是向量,而前者则全是数值;第二,前者没有矩阵的转换过程,所有 变量的数据类型都是NumPy数组。

# 运行测试代码
dataMat, labelMat = loadDataSet()
weigths = stocGradAscent0(array(dataMat), labelMat)
plotBestFit(weigths)
print("w0: %f, w1: %f, W2: %f" % (weigths[0], weigths[1], weigths[2]))

        随机梯度上升算法在上述数据集上的执行结果,最佳拟合直线并非最佳分类线,可以看出拟合曲线出现了很大的偏差。

        如上图,展示了随机梯度上升算法在200次迭代过程中回归系数的变化情况。上图中的X2只经过了50次迭代就达到了稳定值,但X1和X0则需要更多次的迭代。在大的波动停止后,还有一些小的周期性波动。产生这种现象的原因是存在一些不能正确分类的样本点,在每次迭代时会引发系数的剧烈改变。 

7、改进的随机梯度上升算法

# 改进的随机梯度上升算法
def stocGradAscent1(dataMatrix, classLabels, numIter=150):  # dataMatIn数据集、classLabels数据标签、numIter迭代次数
    m, n = shape(dataMatrix)                                # 获取数据集矩阵的大小,m为行数,n为列数
    weights = ones(n)                                       # 所以初始化为1
    for j in range(numIter):
        dataIndex = list(range(m))                          # 创建数据下标列表
        for i in range(m):
            alpha = 4 / (1.0 + j + i) + 0.0001              # apha目标移动的步长,每次迭代调整
            randIndex = int(random.uniform(0, len(dataIndex)))  # 随机选取更新样本
            h = sigmoid(sum(dataMatrix[randIndex] * weights))   # 矩阵相乘,计算sigmoid函数
            error = classLabels[randIndex] - h                  # 计算误差
            weights = weights + alpha * error * dataMatrix[randIndex]  # 矩阵相乘,更新权重
            del (dataIndex[randIndex])                                 # 删除已使用过的样本
    return weights
# 测试
dataMat, labelMat = loadDataSet()
weigths = stocGradAscent1(array(dataMat), labelMat)
plotBestFit(weigths)
print("w0: %f, w1: %f, W2: %f" % (weigths[0], weigths[1], weigths[2]))

         改进的第一个方面在alpha = 4 / (1.0 + j + i) + 0.0001,alpha在每次迭代的时候都会调整,另外,虽然alpha会随着迭代次数不断减小,但永远不会减小到0。必须这样做的原因是为了保证在多次迭代之后新数据仍然具有一定的影响。 如果要处理的问题是动态变化的,那么可以适当加大上述常数项,来确保新的值获得更大的回归系数。另一点值得注意的是,在降低alpha的函数中,alpha每次减少1/(j+i) ,其中j是迭代次数, i是样本点的下标。

        第二个改进的地方在randIndex = int(random.uniform(0, len(dataIndex)))处,这里通过随机选取样本来更新回归系数,每一次都是迭代未用过的样本点。这种方法将减少周期性的波动,计算量减少了,而且从上述的运行结果可以看出,回归效果挺好。 

        如上图,使用样本随机选择和alpha动态减少机制的随机梯度上升算法stocGradAscent1() 所生成的系数收敛示意图。该方法比采用固定alpha的方法收敛速度更快。

四、实例:从疝气病症状预测病马的死亡率

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

实例:从疝气病症状预测病马的死亡率

(1) 收集数据:给定数据文件。

(2) 准备数据:用Python解析文本文件并填充缺失值。

(3) 分析数据:可视化并观察数据。

(4) 训练算法:使用优化算法,找到最佳的系数。

(5) 测试算法:为了量化回归的效果,需要观察错误率。根据错误率决定是否回退到训练 阶段,通过改变迭代的次数和步长等参数来得到更好的回归系数。

(6) 使用算法:实现一个简单的命令行程序来收集马的症状并输出预测结果并非难事,这 可以做为留给读者的一道习题。

1、准备数据:处理数据中的缺失值

 病马的训练数据,如下形式存储在文本文件中:

2、测试算法:使用梯度上升算法进行分类

# 分类函数
def classifyVector(inX, weights):
    prob = sigmoid(sum(inX * weights))   # 计算sigmoid值
    if prob > 0.5:                       # 概率大于0.5,返回分类结果1.0
        return 1.0
    else:                                # 概率小于等于0.5,返回分类结果0.0
        return 0.0
def colicTest1():
    # 读取测试集和训练集,并对数据进行格式化处理
    frTrain = open('horseColicTraining.txt')      # 读取训练集文件
    frTest = open('horseColicTest.txt')           # 读取测试集文件
    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 =  gradAscent(array(trainingSet), trainingLabels)
    errorCount = 0                                # 错误数
    numTestVec = 0.0
    for line in frTest.readlines():               # 遍历每行数据
        numTestVec += 1.0                         # 测试集数量加1
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(array(lineArr), trainWeights)) != int(currLine[21]):
            errorCount += 1                        # 预测结果与真值不一致,错误数加1
    errorRate = (float(errorCount) / numTestVec)   # 计算错误率
    print("测试的错误率为: %f" % errorRate)
    return errorRate
# 求结果的平均值
def multiTest():
    numTests = 10
    errorSum = 0.0
    for k in range(numTests):
        errorSum += colicTest1()
    print("在 %d 迭代之后, 平均错误率为: %f" % (numTests, errorSum / float(numTests)))

测试结果: 

 从上面的结果可以看到,10次迭代之后的平均错误率为28%。

3、测试算法:使用改进后的随机梯度上升算法进行分类

        使用Logistic 回归方法进行分类并不需要做很多工作,所需做的只是把测试集上每个特征向量乘以最优化方法得来的回归系数,再将该乘积结果求和,最后输入到Sigmoid函数中即可。如果对应的Sigmoid值大于0.5就预测类别标签为1,否则为0。

# 分类函数
def classifyVector(inX, weights):
    prob = sigmoid(sum(inX * weights))   # 计算sigmoid值
    if prob > 0.5:                       # 概率大于0.5,返回分类结果1.0
        return 1.0
    else:                                # 概率小于等于0.5,返回分类结果0.0
        return 0.0
#  基于改进后的随机梯度上升算法的Logistic分类器
def colicTest():
    # 读取测试集和训练集,并对数据进行格式化处理
    frTrain = open('horseColicTraining.txt')      # 读取训练集文件
    frTest = open('horseColicTest.txt')           # 读取测试集文件
    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 = stocGradAscent1(array(trainingSet), trainingLabels, 1000)
    errorCount = 0                                # 错误数
    numTestVec = 0.0
    for line in frTest.readlines():               # 遍历每行数据
        numTestVec += 1.0                         # 测试集数量加1
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(array(lineArr), trainWeights)) != int(currLine[21]):
            errorCount += 1                        # 预测结果与真值不一致,错误数加1
    errorRate = (float(errorCount) / numTestVec)   # 计算错误率
    print("测试的错误率为: %f" % errorRate)
    return errorRate
# 求结果的平均值
def multiTest():
    numTests = 10
    errorSum = 0.0
    for k in range(numTests):
        errorSum += colicTest()
    print("在 %d 迭代之后, 平均错误率为: %f" % (numTests, errorSum / float(numTests)))

测试结果:

 从上面的结果可以看到,10次迭代之后的平均错误率为34%。

4、实例:实践小结

梯度上升算法:每次更新回归系数所有样本都参与。

  • 优点:分类准确,获取全局最优解
  • 缺点:当样本比较多时,训练速度特别慢
  • 适用场合:样本较少的数据集

随机梯度下降法:每次更新回归系数只有一个样本参与。

  • 优点:训练速度很快
  • 缺点:准确率会降低,并不是朝着整体最优方向进行,容易获取到局部最优解
  • 适用场合:样本非常多的数据集

     本次实验过程,遇到了好几个小问题,比如绘图的时候,拟合曲线不显示的问题,没有注意到x,y的维度问题,导致卡在绘图上半天;还有就是容易忽略变量的数据类型,因为python语言,在定义变量类型这方面是没有严格要求,不像C或C++等语言一样要明确写出来的,所以我老是忘记把数据转换为列表或矩阵,导致过程中一直运行出错。到底是把实验弄明白,做出来了,收获了一种分类方法,继续努力!!!

  • 33
    点赞
  • 237
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
众所周知,人工智能是当前最热门的话题之一, 计算机技术与互联网技术的快速发展更是将对人工智能的研究推向一个新的高潮。 人工智能是研究模拟和扩展人类智能的理论与方法及其应用的一门新兴技术科学。 作为人工智能核心研究领域之一的机器学习, 其研究动机是为了使计算机系统具有人的学习能力以实现人工智能。 那么, 什么是机器学习呢? 机器学习 (Machine Learning) 是对研究问题进行模型假设,利用计算机从训练数据中学习得到模型参数,并最终对数据进行预测和分析的一门学科。 机器学习的用途 机器学习是一种通用的数据处理技术,其包含了大量的学习算法。不同的学习算法在不同的行业及应用中能够表现出不同的性能和优势。目前,机器学习已成功地应用于下列领域: 互联网领域----语音识别、搜索引擎、语言翻译、垃圾邮件过滤、自然语言处理等 生物领域----基因序列分析、DNA 序列预测、蛋白质结构预测等 自动化领域----人脸识别、无人驾驶技术、图像处理、信号处理等 金融领域----证券市场分析、信用卡欺诈检测等 医学领域----疾病鉴别/诊断、流行病爆发预测等 刑侦领域----潜在犯罪识别与预测、模拟人工智能侦探等 新闻领域----新闻推荐系统等 游戏领域----游戏战略规划等 从上述所列举的应用可知,机器学习正在成为各行各业都会经常使用到的分析工具,尤其是在各领域数据量爆炸的今天,各行业都希望通过数据处理与分析手段,得到数据中有价值的信息,以便明确客户的需求和指引企业的发展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DreamWendy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值