Logistic Regression

回顾

在前面线性回归的预测房屋价格的问题中,我们希望可以找到一条很好的线来拟合现有的数据,并使用训练的模型更好的对未知的数据进行预测。而在实际生产环境中,虽然一个样本数据往往具有很多个特征,但并不是希望找到一条拟合的直线或是曲线,而是回答类似于YES/NO的问题,例如:


这里写图片描述

如上表所示,我们希望研究影响一个人得某种疾病的相关因素,分别有 x 1 , x 2 , x 33 x_{1},x_{2},x_{33} x1,x2,x33三个特征:1表示因素存在,0表示不存在; P = 0 P=0 P=0表示不患病, P = 1 P=1 P=1表示患病。如果想要使用线性回归进行解决,因为存在多个特征,自然想到应该使用多元线性回归,但无法使得到的值落在0~1之间,那么Logistic Regression就可以很好的解决这个问题。


Sigmoid函数

Sigmoid函数

Sigmoid函数:A sigmoid function is a mathematical function having a characteristic “S”-shaped curve or sigmoid curve. Often, sigmoid function refers to the special case of the logistic function shown in the first figure and defined by the formula,它的函数形式是: S ( x ) = 1 1 + e − x = e x e x + 1 S(x)=\frac{1}{1+e^{-x}}=\frac{e^x}{e^x+1} S(x)=1+ex1=ex+1ex

函数图像如下:


这里写图片描述

它的取值在0~1之间,可以很好的满足Logistic回归的需求。


Logistic Regression

Logistic Regression模型的主要思想是:根据现有数据对分类边界线建立回归模型,以此进行分类

一般过程为;

  • 收集数据
  • 准备数据:要求数据类型是数值型
  • 分析数据
  • 训练算法:找到最佳的回归系数
  • 测试算法
  • 使用算法

Logistic Regression模型可以表示为:在给定特征向量 x = ( x 1 , x 2 , . . . , x n ) x=(x_{1},x_{2},...,x_{n}) x=(x1,x2,...,xn)时,条件概率 P ( y = 1 ∣ x ) P(y=1|x) P(y=1x)为根据观测量某事件 y y y发生的概率,即 P ( y = 1 ∣ x ) = π ( x ) = 1 1 + e − x P(y=1|x)=\pi(x)=\frac{1}{1+e^{-x}} P(y=1x)=π(x)=1+ex1

同理不发生的概率可以表示为: P ( y = 1 ∣ x ) = 1 − π ( x ) = 1 1 + e x P(y=1|x)=1- \pi(x)=\frac{1}{1+e^{x}} P(y=1x)=1π(x)=1+ex1

还可以求出发生比 o d d s = P ( y = 1 ∣ x ) P ( y = o ∣ x ) = e x odds = \frac{P(y=1|x)}{P(y=o|x)}=e^x odds=P(y=ox)P(y=1x)=ex

将回归函数带入Sigmoid函数得: h θ ( x ) = g ( θ T x ) = 1 1 + e − θ T x h_{\theta}(x)=g(\theta^{T}x)=\frac{1}{1+e^{-\theta^{T}x}} hθ(x)=g(θTx)=1+eθTx1

假设概率分布: P ( y = 1 ∣ x ; θ ) = h θ ( x ) P ( y = 0 ∣ x ; θ ) = 1 − h θ ( x ) P(y=1|x;\theta)=h_{\theta}(x) \\ P(y=0|x;\theta)=1-h_{\theta}(x) P(y=1x;θ)=hθ(x)P(y=0x;θ)=1hθ(x)

那么概率的表达就变成了: p ( y ∣ x ; θ ) = ( h θ ( x ) ) y ( 1 − h θ ( x ) ) 1 − y p(y|x;\theta)=(h_{\theta}(x))^{y}(1-h_{\theta}(x))^{1-y} p(yx;θ)=(hθ(x))y(1hθ(x))1y

由于各个样本之间相互独立,因此联合分布就等于各自边缘分布的乘积,故似然函数为:
L ( θ ) = p ( y ⃗ ∣ X ; θ ) = ∏ i = 1 m p ( y ( i ) ∣ x ( i ) ; θ ) = ∏ i = 1 m ( h θ ( x ( i ) ) ) y ( i ) ( 1 − h θ ( x ( i ) ) ) 1 − y ( i ) \begin{aligned} L(\theta) &=p(\vec{y} | X ; \theta) \\ &=\prod_{i=1}^{m} p\left(y^{(i)} | x^{(i)} ; \theta\right) \\ &=\prod_{i=1}^{m}\left(h_{\theta}\left(x^{(i)}\right)\right)^{y^{(i)}}\left(1-h_{\theta}\left(x^{(i)}\right)\right)^{1-y^{(i)}} \end{aligned} L(θ)=p(y X;θ)=i=1mp(y(i)x(i);θ)=i=1m(hθ(x(i)))y(i)(1hθ(x(i)))1y(i)

利用最大似然估计计算的过程: ℓ ( θ ) = log ⁡ L ( θ ) = ∑ i = 1 m y ( i ) log ⁡ h ( x ( i ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − h ( x ( i ) ) ) ∂ ∂ θ j ℓ ( θ ) = ( y 1 g ( θ T x ) − ( 1 − y ) 1 1 − g ( θ T x ) ) ∂ ∂ θ j g ( θ T x ) = ( y 1 g ( θ T x ) − ( 1 − y ) 1 1 − g ( θ T x ) ) g ( θ T x ) ( 1 − g ( θ T x ) ) ∂ ∂ θ j θ T x = ( y ( 1 − g ( θ T x ) ) − ( 1 − y ) g ( θ T x ) ) x j = ( y − h θ ( x ) ) x j \begin{aligned} \ell(\theta) &=\log L(\theta) \\ &=\sum_{i=1}^{m} y^{(i)} \log h\left(x^{(i)}\right)+\left(1-y^{(i)}\right) \log \left(1-h\left(x^{(i)}\right)\right) \\ \frac{\partial}{\partial \theta_{j}} \ell(\theta) &=\left(y \frac{1}{g\left(\theta^{T} x\right)}-(1-y) \frac{1}{1-g\left(\theta^{T} x\right)}\right) \frac{\partial}{\partial \theta_{j}} g\left(\theta^{T} x\right) \\ &=\left(y \frac{1}{g\left(\theta^{T} x\right)}-(1-y) \frac{1}{1-g\left(\theta^{T} x\right)}\right) g\left(\theta^{T} x\right)\left(1-g\left(\theta^{T} x\right)\right) \frac{\partial}{\partial \theta_{j}} \theta^{T} x \\ &=\left(y\left(1-g\left(\theta^{T} x\right)\right)-(1-y) g\left(\theta^{T} x\right)\right) x_{j} \\ &=\left(y-h_{\theta}(x)\right) x_{j} \end{aligned} (θ)θj(θ)=logL(θ)=i=1my(i)logh(x(i))+(1y(i))log(1h(x(i)))=(yg(θTx)1(1y)1g(θTx)1)θjg(θTx)=(yg(θTx)1(1y)1g(θTx)1)g(θTx)(1g(θTx))θjθTx=(y(1g(θTx))(1y)g(θTx))xj=(yhθ(x))xj

接下来所要做的就是通过上面已经得到的结论来求解使得似然函数最大化的参数向量。在实际中有很多方法可供选择,其中比较常用的包括梯度下降法、牛顿法和拟牛顿法等。

1. 梯度上升法
学过了梯度下降法,梯度上升法相对就好学了,它的基本思想是:要找到某函数的最大值,最好的方法就是沿着函数的梯度方向探寻。它的迭代公式为: θ j : = θ j + α ( y ( i ) − h θ ( x ( i ) ) ) x j ( i ) \theta_{j}:=\theta_{j}+\alpha\left(y^{(i)}-h_{\theta}\left(x^{(i)}\right)\right) x_{j}^{(i)} θj:=θj+α(y(i)hθ(x(i)))xj(i)
Repeat until convergence
θ j : = θ j + α ∑ i = 1 m ( y ( i ) − h θ ( x ( i ) ) ) x j ( i ) \theta_{j}:=\theta_{j}+\alpha \sum_{i=1}^{m}\left(y^{(i)}-h_{\theta}\left(x^{(i)}\right)\right) x_{j}^{(i)} θj:=θj+αi=1m(y(i)hθ(x(i)))xj(i)

代码实现为:

#梯度上升法的具体实现
#dataMatIn是2维数组,包含两个特征,每行是一个训练样本,在这里是一个100*3的矩阵
#classLabels是类别标签,是100*1的行向量
def gradAscent(dataMatIn, classLabels):
  
    #将数据转换成numpy矩阵数据类型
    dataMatrix = mat(dataMatIn) 
    #方便计算,须将其转置成列向量            
    labelMat = mat(classLabels).transpose() 
    #得到矩阵的大小
    m,n = shape(dataMatrix)
    #设置学习率和迭代次数
    alpha = 0.001
    maxCycles = 500
    weights = ones((n,1))
    for k in range(maxCycles):              
        h = sigmoid(dataMatrix*weights)    
        error = (labelMat - h)
        #矩阵相乘              
        weights = weights + alpha * dataMatrix.transpose()* error 
    return weights

使用梯度上升法得到一组最佳的回归系数,接下来画出它的决策边界:

#画出数据集和Logistic回归最佳拟合直线
def plotBestFit(weights):
   
    dataMat,labelMat=loadDataSet()
    dataArr = array(dataMat)

    #数据集的容量n
    n = 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 = arange(-3.0, 3.0, 0.1)

    #最佳拟合直线
    y = (-weights[0]-weights[1]*x)/weights[2]
    ax.plot(x, y)
    plt.xlabel('X1'); plt.ylabel('X2');
    plt.show()

结果为:


这里写图片描述

从上图可以看到效果较好,但是这样做仍存在一个问题:在每次迭代更新回归系数时需要遍历整个数据集,如果数据不多时影响似乎不大,但如果数据集很大很大时,计算的复杂度就太高了。

2. 随机梯度上升法
实现过程为:

  • 所有回归系数初始化为1
  • 对数据集中每个样本计算样本的梯度
  • 使用 α ∗ g r a d i e n t \alpha * gradient αgradient来更新回归系数值
  • 返回系数值

实现代码为:

#随机梯度上升算法,一次仅用一个样本点来更新回归系数
#初始化所有的回归系数均为1,使用alpha*gradient更新回归系数值
def stocGradAscent0(dataMatrix, classLabels):
    m,n = shape(dataMatrix)
    alpha = 0.01
    #初始化所有回归系数为1
    weights = ones(n)   
    for i in range(m):
        h = sigmoid(sum(dataMatrix[i]*weights))
        error = classLabels[i] - h
        weights = weights + alpha * error * dataMatrix[i]
    return weights

分类效果:


这里写图片描述

但是实际中数据集中总是存在一些不能正确分类的点,而且收敛的速度也不够快,那么就有了改进的随机梯度上升算法:

#改进的随机梯度上升算法
def stocGradAscent2(dataMatrix, classLabels, numIter=150):
    m,n = shape(dataMatrix)
    weights = ones(n)   
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            #alpha每次迭代时需要调整
            alpha = 4/(1.0+j+i)+0.0001    
            #随机选取样本来更新回归系数
            randIndex = int(random.uniform(0,len(dataIndex)))
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * error * dataMatrix[randIndex]
            del(dataIndex[randIndex])
    return weights

分类结果:


这里写图片描述

《Machine Learning inAction》
机器学习之详解Logistic回归



1. sklearn中关于LogisticRegression官方API的学习

# -*- coding: UTF-8 -*-
import numpy as np # 快速操作结构数组的工具
import pandas as pd # 数据分析处理工具

# ## logistic回归
#
# https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression.set_params
#
# 在多分类问题中,若mutil_class为ovr,则学习算法使用one-vs-rest模式,若multi_class为multinomial,则使用交叉熵损失
# LogisticRegression使用liblinear、newton-cg、lbfgs求解器来实现正则化的Logistic回归,可以处理密集和稀疏输入
# 使用包含64位浮点数的C有序数组或CSR矩阵以获得最佳性能; 任何其他输入格式将被转换(和复制)
#
# paras:
#       penalty:str、l1|l2,默认l2,指定正则化中使用的惩罚项,newton-cg、sag和lbfgs仅支持l2惩罚项
#       dual:bool,默认False,对偶还是原始方法,dual只适用于正则化项为l2 liblinear的情况,通常样本数大于特征数的情况下,默认False
#       tol:float,可选,指定迭代终止的误差范围
#       c:float,默认1.0,正则化项系数的倒数,默认1,越小正则化强度越大
#       fit_intercept:bool,默认True,是否存在截距,若存在要加到决策函数
#       intercept_scaling:float,默认1,仅在正则化项为liblinear且fit_intercept为True时有用
#       class_weight:dict|balanced,默认None,class_weight参数用于标示分类模型中各种类型的权重,可以不输入,即不考虑权重,或者说所有类型的权重一样。
#                    如果选择输入的话,可以选择balanced让类库自己计算类型权重,或者我们自己输入各个类型的权重
#       random_state:int,RandomState instance|None,可选,默认None,随机数种子,仅在正则化优化算法为sag,liblinear时有用
#       max_iter:int,可选,算法收敛的最大迭代次数,仅在正则化优化算法为newton-cg, sag and lbfgs 才有用
#       multi_class:str,ovr|multinomial,默认ovr
#       verbose:int,默认0,对于liblinear和lbfgs求解器,将verbose设置为任何正数以表示详细程度
#       warm_start:bool,默认False,设置为True时,重用上一次调用的解决方案以适合初始化,否则,只需擦除以前的解决方案。对于liblinear解算器没用。
#       n_jobs:int,默认1,若multi_class ='ovr'“,则在对类进行并行化时使用的CPU核心数。solver无论是否指定了“multi_class”,当设置为“liblinear” 时,将忽略此参数
#       solver : str, {‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’}, 默认 ‘liblinear’.logistic回归损失函数的优化算法,一共4种
#
# attribus:
#       coef_
#       intercept_
#       n_iter
#
# methods:
#       decision_function(X):预测样本的可信度得分
#       densify():将系数矩阵转换成密集阵列格式
#       fit(X, y[, sample_weight]):根据给定的数据拟合模型
#       get_params([deep]):获取参数
#       predict(X):预测样本X的类标签
#       predict_log_proba(X):估计对数概率
#       predict_proba(X):概率估计
#       score(X, y[, sample_weight]):返回给定数据和标签的平均准确度
#       set_params(**params):设置参数
#       sparsify():将系数矩阵转换成稀疏格式
# 样本数据集,第一列为x1,第二列为x2,第三列为分类(三种类别)
data=[
        [-2.68420713, 0.32660731, 0],[-2.71539062, -0.16955685, 0],[-2.88981954, -0.13734561, 0],[-2.7464372, -0.31112432, 0],[-2.72859298, 0.33392456, 0],
        [-2.27989736, 0.74778271, 0],[-2.82089068, -0.08210451, 0],[-2.62648199, 0.17040535, 0],[-2.88795857, -0.57079803, 0],[-2.67384469, -0.1066917, 0],
        [-2.50652679,0.65193501,0],[-2.61314272,0.02152063,0],[-2.78743398,-0.22774019,0],[-3.22520045,-0.50327991,0],[-2.64354322,1.1861949,0],
        [-2.38386932,1.34475434,0],[-2.6225262,0.81808967,0],[-2.64832273,0.31913667,0],[-2.19907796,0.87924409,0],[-2.58734619,0.52047364,0],
        [1.28479459, 0.68543919, 1],[0.93241075, 0.31919809, 1],[1.46406132, 0.50418983, 1],[0.18096721, -0.82560394, 1],[1.08713449, 0.07539039, 1],
        [0.64043675, -0.41732348, 1],[1.09522371, 0.28389121, 1],[-0.75146714, -1.00110751, 1],[1.04329778, 0.22895691, 1],[-0.01019007, -0.72057487, 1],
        [-0.5110862,-1.26249195,1],[0.51109806,-0.10228411,1],[0.26233576,-0.5478933,1],[0.98404455,-0.12436042,1],[-0.174864,-0.25181557,1],
        [0.92757294,0.46823621,1],[0.65959279,-0.35197629,1],[0.23454059,-0.33192183,1],[0.94236171,-0.54182226,1],[0.0432464,-0.58148945,1],
        [2.53172698, -0.01184224, 2],[1.41407223, -0.57492506, 2],[2.61648461, 0.34193529, 2],[1.97081495, -0.18112569, 2],[2.34975798, -0.04188255, 2],
        [3.39687992, 0.54716805, 2],[0.51938325, -1.19135169, 2],[2.9320051, 0.35237701, 2],[2.31967279, -0.24554817, 2],[2.91813423, 0.78038063, 2],
        [1.66193495,0.2420384,2],[1.80234045,-0.21615461,2],[2.16537886,0.21528028,2],[1.34459422,-0.77641543,2],[1.5852673,-0.53930705,2],
        [1.90474358,0.11881899,2],[1.94924878,0.04073026,2],[3.48876538,1.17154454,2],[3.79468686,0.25326557,2],[1.29832982,-0.76101394,2],
]
# 样本数据集,第一列为x1,第二列为x2,第三列为分类(2种类别)
data1=[
    [-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],
    [0.406704,7.067335,1],
    [0.667394,12.741452,0],
    [-2.460150,6.866805,1],
    [0.569411,9.548755,0],
    [-0.026632,10.427743,0],
    [0.850433,6.920334,1],
    [1.347183,13.175500,0],
    [1.176813,3.167020,1],
    [-1.781871,9.097953,0],
    [-0.566606,5.749003,1],
    [0.931635,1.589505,1],
    [-0.024205,6.151823,1],
    [-0.036453,2.690988,1],
    [-0.196949,0.444165,1],
    [1.014459,5.754399,1]
]
#生成X和y矩阵
dataMat = np.mat(data)
y = dataMat[:,2]   # 类别变量
b = np.ones(y.shape)  # 添加全1列向量代表b偏量
X = np.column_stack((b, dataMat[:,0:2]))  # 特征属性集和b偏量组成x
X = np.mat(X)


# 特征数据归一化
#import sklearn.preprocessing as preprocessing   #sk的去均值和归一化
#scaler=preprocessing.StandardScaler()
#X = scaler.fit_transform(X)   # 对特征数据集去均值和归一化,可以加快机器性能
#X = np.mat(X)
#print(X)

# ========逻辑回归========

from sklearn import metrics
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X, y)
print('逻辑回归模型:\n',model)
# 使用模型预测
predicted = model.predict(X)   #预测分类
answer = model.predict_proba(X)  #预测分类概率
print(answer)

import matplotlib.pyplot as plt

# 绘制边界和散点
# 先产生x1和x2取值范围上的网格点,并预测每个网格点上的值。
h = 0.02
x1_min, x1_max = X[:,1].min() - .5, X[:,1].max() + .5
x2_min, x2_max = X[:,2].min() - .5, X[:,2].max() + .5
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, h), np.arange(x2_min, x2_max, h))
testMat = np.c_[xx1.ravel(), xx2.ravel()]   #形成测试特征数据集
testMat = np.column_stack((np.ones(((testMat.shape[0]),1)),testMat))  #添加第一列为全1代表b偏量
testMat = np.mat(testMat)
Z = model.predict(testMat)

# 绘制区域网格图
Z = Z.reshape(xx1.shape)
plt.pcolormesh(xx1, xx2, Z, cmap=plt.cm.Paired)


# 绘制散点图 参数:x横轴 y纵轴,颜色代表分类。x图标为样本点,.表示预测点
plt.scatter(X[:,1].flatten().A[0], X[:,2].flatten().A[0],c=y.flatten().A[0],marker='x')   # 绘制样本数据集
plt.scatter(X[:,1].flatten().A[0], X[:,2].flatten().A[0],c=predicted.tolist(),marker='.') # 绘制预测数据集

# 绘制x轴和y轴坐标
plt.xlabel("x")
plt.ylabel("y")

# 显示图形
plt.show()

输出
二分类的结果:第 i i i列表示属于第 i i i类的概率


在这里插入图片描述
在这里插入图片描述

多分类的结果:


在这里插入图片描述
在这里插入图片描述

参考:

python机器学习库sklearn——逻辑回归
官方API

英文文档
中文文档

2. 逻辑分类、线性回归、线性分类、逻辑回归的区别?
在前面的线性回归中,我们都是通过计算回归系数,通过回归系数线性组合属性预测我们的结果。在线性回归中我们使用均方误差函数作为我们的损失函数,我们的计算就是要最小化损失函数,其实这里我们假定误差服从高斯分布,在前面也可以看到。比如在二分类问题中,我们通过比较计算结果和给定的阈值的大小关系来确定分类的结果。

至于逻辑回归,它是逻辑分类的一种。在逻辑分类中我们的函数为 y = f ( x ∗ w + b ) y=f(x∗w+b) y=f(xw+b),当f为sigmoid函数时,就是逻辑回归。它就是在线性回归的基础上,再经过sigmoid这个非线性函数,将值转化为分类的概率,实际上是采用的是伯努利分布来分析误差。关于sigmoid函数,前面已经学习过啦!

伯努利分布指的是对于随机变量X有, 参数为p(0<p<1),如果它分别以概率p和1-p取1和0为值。EX= p,DX=p(1-p)。伯努利试验成功的次数服从伯努利分布,参数p是试验成功的概率。伯努利分布是一个离散型机率分布,是N=1时二项分布的特殊情况

逻辑分类属于线性模型。逻辑分类包含逻辑回归、一般线性回归。逻辑回归为二分类问题,一般线性回归为多分类问题。一般线性回归就是用指数分布来处理噪声模型的方法。一般使用softmax函数来处理多分类问题。

3. 逻辑分类器的构成
我们主要使用线性回归来估值,使用逻辑分类来进行分类预测。具体我们可以通过下图来看一下:
在这里插入图片描述

假设我们已经知道了 W W W b b b,逻辑回归可以归结为三步:

  • 计算线性函数的输出值:根据已知的 w w w b b b,给定 x x x,经过计算后,得到一个输出值 h = w x + b h=wx+b h=wx+b h h h也是 n n n维的,每一维可以近似认为是待测对象偏向于每种分类的成分
  • 将线性函数的输出结果转化为分类概率:得到了线性函数的输出结果,还需要将每一个维度转化为对象属于每个分类的概率。使得每个概率在0-1之间,将输出结果转化为概率的函数我们称为阶跃函数,我们常用的为sigmoid函数,来进行二分类,用softmax函数来进行多分类。
  • 将分类概率转为为类别:当属于某分类的概率最大时,就可以判定为数据该分类。

4. 逻辑回归的特征为什么要先离散化?

  • 离散特征的增加和减少都很容易,易于模型的快速迭代
  • 稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展
  • 离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;
  • 逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合
  • 离散化后可以进行特征交叉,由 M + N M+N M+N个变量变为 M ∗ N M*N MN个变量,进一步引入非线性,提升表达能力;
  • 特征离散化后,模型会更稳定
  • 特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值