吴恩达逻辑回归总结

吴恩达逻辑回归总结

数据集地址:


课后习题题目

1、学生录取
  在训练的初始阶段,我们将要构建一个逻辑回归模型来预测,某个学生是否被大学录取。
  设想你是大学相关部分的管理者,想通过申请学生两次测试的评分,来决定他们是否被录取。
  现在你拥有之前申请学生的可以用于训练逻辑回归的训练样本集。对于每一个训练样本,你有他们两次测试的评分和最后是被录取的结果。
2、线性不可分案例:假设你是工厂的生产主管,你有一些芯片在两次测试中的测试结果,测试结果决定是否芯片要被接受或抛弃。你有一些历史数据,帮助你构建一个逻辑回归模型。。

一、吴恩达逻辑回归理解

1.逻辑回归

  逻辑回归主要是主要是用来处理分类任务,分类又包含二分类(Binary classification)和多分类(Multi-classclassification),逻辑回归主要用来处理二分类问题,在二分类分问题中又分为线性可分类问题和线性不可分类问题。

在这里插入图片描述

2.线性可分

在这里插入图片描述

2.1 假设函数

  在线性回归模型问题中,我们只能预测连续的值,对于分类问题,我们需要输出0或1,即当假设函数大于等于0.5时,预测y=1,当假设函数小于0.5时,预测y=0。当假设函数超过1时,再用0.5作为阈值来预测目标函数便不恰当。为了使得假设函数的值可以超越[0,1]的范围,且该模型的输出变量始终处于[0,1]之间,引入逻辑函数g(Logistics Function)是一个常用的逻辑函数为S型函数(Sigmoid Function)。

假设函数(Hypothesis): h θ ( x ) = g ( X θ )   \\{h_\theta }(x) = g(X\theta )\, hθ(x)=g(Xθ)
即: h θ ( x ) = g ( θ 0 + θ 1 x 1 + θ 2 x 2 )   \\{h_\theta }(x) = g({\theta _0} + {\theta _1}{x_1} + {\theta _2}{x_2} )\, hθ(x)=g(θ0+θ1x1+θ2x2)
引入逻辑函数(Sigmoid函数): g ( z ) = 1 1 + e − z   \\g(z) = \frac{{\rm{1}}}{{{\rm{1 + }}{e^{ - z}}}}\, g(z)=1+ez1
在这里插入图片描述

2.2 代价函数(Cost Function)

线性回归的代价函数为:
J ( θ ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2   \\J(\theta ) = \frac{{\rm{1}}}{{{\rm{2m}}}}\sum\limits_{i = 1}^m {{{({h_\theta }({x^{(i)}}) - {y^{(i)}})}^2}} \, J(θ)=2m1i=1m(hθ(x(i))y(i))2
重新定义逻辑回归的代价函数:
J ( θ ) = 1 m ∑ i = 1 m C o s t ( h θ ( x ( i ) ) , y ( i ) )   \\J(\theta ) = \frac{{\rm{1}}}{{\rm{m}}}\sum\limits_{i = 1}^m {Cost({h_\theta }({x^{(i)}}),} {y^{(i)}})\, J(θ)=m1i=1mCost(hθ(x(i)),y(i))
C o s t ( h θ ( x ) , y ) = { − log ⁡ ( h θ ( x ) ) i f y = 1 − log ⁡ ( 1 − h θ ( x ) ) i f y = 0   \\Cost({h_\theta }({x}),{y}) = \left\{ \begin{array}{l}- \log ({h_\theta }(x))&if&y=1\\- \log (1 - {h_\theta }(x)) &if&y=0 \end{array} \right.\, Cost(hθ(x),y)={log(hθ(x))log(1hθ(x))ifify=1y=0
这个式子可以合并成:
C o s t ( h θ ( x ) , y ) = − y × log ⁡ ( h θ ( x ) ) − ( 1 − y ) × log ⁡ ( 1 − h θ ( x ) )   \\Cost({h_\theta }(x),y) = - y{\rm{ \times }}\log ({h_\theta }(x)) - (1 - y){\rm{ \times }}\log (1 - {h_\theta }(x))\, Cost(hθ(x),y)=y×log(hθ(x))(1y)×log(1hθ(x))
即,逻辑回归的代价函数:
J ( θ ) = − 1 m ∑ i = 1 m [ y ( i ) log ⁡ ( h θ ( x ( i ) ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − h θ ( x ( i ) ) ) ]   \\J(\theta ) = - \frac{{\rm{1}}}{{\rm{m}}}\sum\limits_{i = 1}^m {[{y^{(i)}}\log ({h_\theta }({x^{(i)}}))} + (1 - {y^{(i)}})\log (1 - {h_\theta }({x^{(i)}}))]\, J(θ)=m1i=1m[y(i)log(hθ(x(i)))+(1y(i))log(1hθ(x(i)))]
注 : h θ ( x ( i ) )   : 预 测 值 , y ( i )   : 真 实 值 \\注:{h_\theta }({x^{(i)}})\,:预测值,{y^{(i)}}\,:真实值 hθ(x(i))y(i):

2.3 梯度下降(Gradient Descent)

  根据上面的代价函数,为了拟合出参数,需要找出尽量让J(θ)取得最小值的参数θ。
  如果给出一个新的样本,假如某个特征x,我们可以通过拟合训练样本的参数θ,来输出对假设的预测,这个输出实质上就是这个概率值:p(y=1|x;θ),就是在X=x,Θ=θ时,y=1的概率,说白了就是个条件概率。

  梯度:
∂ J ( θ ) ∂ θ j = 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i )   \\\frac{{\partial J(\theta )}}{{\partial {\theta _j}}} = \frac{{\rm{1}}}{{\rm{m}}}\sum\limits_{i = 1}^m {({h_\theta }({x^{(i)}}) - {y^{(i)}})} x_j^{(i)}\, θjJ(θ)=m1i=1m(hθ(x(i))y(i))xj(i)
  虽然这里的梯度和线性回归的梯度很像,但这里的假设函数却是不一样的。

2.4 决策边界

在这里插入图片描述  所谓决策边界即为Xθ = 0的一条直线,即:
θ 0 + θ 1 x 1 + θ 2 x 2 = 0   \\{\theta _0} + {\theta _1}{x_1} + {\theta _2}{x_2} = 0\, θ0+θ1x1+θ2x2=0
  即:
x 2 = − θ 0 θ 2 − θ 1 θ 2 x 1   \\{x_2} = - \frac{{{\theta _0}}}{{{\theta _2}}} - \frac{{{\theta _1}}}{{{\theta _2}}}{x_1}\, x2=θ2θ0θ2θ1x1
  说白了,前面的一系列操作就是为了求出θ0、θ1、θ2。

3. 线性不可分

在这里插入图片描述  上面的图片可以看出,这个数据集不能像之前一样使用直线将两部分分割。而逻辑回归只适用于线性的分割,所以,这个数据集不适合直接使用逻辑回归。

3.1 特征映射

  特征映射用于制造非线性回归复杂属性。通过循环将原本的输入值矩阵扩展成多项展开式的形式。这样做能够获得不同于线性回归的更加复杂、合理的目标函数。

二、代码演示

1.引入库

import numpy as np
import pandas as pd
import matplotlib.pyplot as pltt
import scipy.optimize as opt

1、线性可分代码演示

1.读入数据

path = 'K:\python\吴恩达机器学习python作业代码\code\ex2-logistic regression/ex2data1.txt'
data1 = pd.read_csv(path,header=None,names=['Exam 1', 'Exam 2', 'Admitted'])
                                                                                      # 读取数据集data1
# print("查看数据集data1:",data1)

2.数据可视化

数据预处理:(将通过的和未通过的分开)

positive = data1[data1['Admitted'].isin([1])]                                        # 将Admitted为1(即录取)的提取出来
negative = data1[data1['Admitted'].isin([0])]                                        # 将Admitted为0(即未录取)的提取出来
# print('查看positive',positive)
# print('查看negative',negative)

数据可视化

fig, ax = plt.subplots(figsize=(9,6))                                                # 设置图片尺寸
ax.scatter(positive['Exam 1'], positive['Exam 2'],
           s = 25,c = 'b',marker = 'o',label = 'Admitted')                           # 绘制positive的图像,s表示点的尺寸
ax.scatter(negative['Exam 1'], negative['Exam 2'],
           s = 25,c = 'r',marker = 'x',label = 'Not Admitted')                       # 绘制negative的图像
ax.set_title("Datainit", fontsize=14)                                                # 图名字,字号14
ax.set_xlabel("Exam 1 Score", fontsize=10)                                           # x轴标签,字号10
ax.set_ylabel("Exam 2 Score", fontsize=10)                                           # y轴标签,字号10
ax.tick_params(axis = 'both', which = 'major', labelsize = 10)
ax.legend(loc='lower right')
plt.show()

在这里插入图片描述

3.构造数据集(数据预处理)

def get_Xy(data1):
    data1.insert(0, 'Ones', 1)                                 # 在数据第一列插入1
    cols = data1.shape[1]                                      # 列数
    X = data1.iloc[:, :cols - 1]                               # X是data1里的除最后列
    y = data1.iloc[:, cols - 1:cols]                           # y是data1最后一列
    theta = np.zeros(3)                                        # 初始化一个一行三列的0矩阵
    theta = theta.reshape(1,3)                                 # 将0矩阵转化为一个行3列的矩阵
    X = np.array(X.values)
    y = np.array(y.values)
    return X,y,theta
X,y,theta = get_Xy(data1)                                    # 获取X,y,theta的数据集
print(X.shape,y.shape,theta.shape)                           # 查看X,y,theta的维度

维度:(100, 3) (100, 1) (1, 3)

4.构造sigmoid函数

def sigmoid(z):
    return 1 / (1 + np.exp(-z))                                        # 定义igmoid Function

sigmoid函数可视化:

fig, ax = plt.subplots(figsize=(9,6))                                  # 设置图片尺寸
x1 = np.arange(-10, 10, 0.1)
ax.plot(x1, sigmoid(x1), c='r',label = 'g(z)')                         # 绘制g(z)的图像
ax.set_title("Sigmoid Function", fontsize=14)                          # 图名字,字号14
ax.set_xlabel("z", fontsize=10)                                        # x轴标签,字号10
ax.set_ylabel("g(z)", fontsize=10)                                     # y轴标签,字号10
ax.tick_params(axis = 'both', which = 'major', labelsize = 10)         # 定义主参数
ax.legend(loc='lower right')
plt.show()

在这里插入图片描述

5.代价函数CostFunction(损失函数)

def costFunction(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    return np.sum(first - second) / (len(X))
cost_init = costFunction(theta, X, y)
print(cost_init)

0.6931471805599453

6.梯度下降

实现梯度计算的函数(并没有更新θ)

def gradient(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:, i])
        grad[i] = np.sum(term) / len(X)

    return grad

参数更新(科学计算法):
  在此前的线性回归中,自己写代码实现的梯度下降。当时写了一个代价函数、计算了他的梯度,然后对他执行了梯度下降的步骤。这里,我们调用一个已有的库。这就是说,我们不用自己定义迭代次数和步长,功能会直接告诉我们最优解。Python中我们可以用scipy.optimize.fmin_tnc可以帮我们作这样的事。

result = opt.fmin_tnc(func=costFunction, x0=theta, fprime=gradient, args=(X, y))
print(result)

(array([-25.16131872, 0.20623159, 0.20147149]), 36, 0)

cost_init1 = costFunction(result[0], X, y)
print(cost_init1)

0.20349770158947425

7.决策边界

fig, ax = plt.subplots(figsize=(9,6))
ax.set_title("Decision Boundary", fontsize=14)                                    # 图名字,字号14
ax.set_xlabel("Exam 1 Score", fontsize=10)                                        # x轴标签,字号10
ax.set_ylabel("Exam 2 Score", fontsize=10)                                        # y轴标签,字号10
plotting_x1 = np.linspace(30, 100, 100)                                           # 定义x轴参数,即自变量
plotting_x2 = (- result[0][0] - result[0][1] * plotting_x1) / result[0][2]        # 建立函数关系
ax.plot(plotting_x1, plotting_x2,c = 'g', label='Prediction')
ax.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Not Admitted')
ax.legend(loc='lower left')
plt.show()

在这里插入图片描述

8.评价逻辑回归模型

8.1、评价方法1

当exam1 = 45,exam2 = 85时,求其录取的概率P1:

def hfunc1(theta, X):
    return sigmoid(np.dot(theta.T, X))
P1 = hfunc1(result[0],[1,45,85])
print('P1=',P1)

P1= 0.776290625526598

8.1、评价方法2

  这种评价θ的方法是看模型在训练集上的正确率怎样。写一个predict的函数,给出数据以及参数后,会返回“1”或者“0”。然后再把这个predict函数用于训练集上,看准确率怎样。
定义预测函数:

def predict(theta, X):
    probability = sigmoid(X * theta.T)
    return [1 if x >= 0.5 else 0 for x in probability]

统计预测正确率:

theta_min = np.matrix(result[0])
predictions = predict(theta_min, X)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
P = '{0}%'.format(accuracy)
print ('准确率P=',P)

准确率P = 89%

2、线性不可分代码演示

1.读入数据

path =  'K:\python\吴恩达机器学习python作业代码\code\ex2-logistic regression/ex2data2.txt'
data2 = pd.read_csv(path, header=None, names=['Test 1', 'Test 2', 'Accepted'])
# print("查看数据集data2:",data2)

2.数据可视化

数据预处理:(将通过的和未通过的分开)

positive2 = data2[data2['Admitted'].isin([1])]                                        # 将Admitted为1(即录取)的提取出来
negative2 = data2[data2['Admitted'].isin([0])]                                        # 将Admitted为0(即未录取)的提取出来
# print('查看positive',positive)
# print('查看negative',negative)

数据可视化

fig, ax = plt.subplots(figsize=(9,6))                                                # 设置图片尺寸
ax.scatter(positive2['Test 1'], positive2['Test 2'],
           s = 25,c = 'b',marker = 'o',label = 'Admitted')                           # 绘制positive的图像,s表示点的尺寸
ax.scatter(negative2['Test 1'], negative2['Test 2'],
           s = 25,c = 'r',marker = 'x',label = 'Not Admitted')                       # 绘制negative的图像
ax.set_title("Datainit", fontsize=14)                                                # 图名字,字号14
ax.set_xlabel("Test 1 Score", fontsize=10)                                           # x轴标签,字号10
ax.set_ylabel("Test 2 Score", fontsize=10)                                           # y轴标签,字号10
ax.tick_params(axis = 'both', which = 'major', labelsize = 10)
ax.legend(loc='lower right')
plt.show()

在这里插入图片描述

3.特征映射

degree = 6
x1 = data2['Test 1']
x2 = data2['Test 2']
data2.insert(3, 'Ones', 1)

for i in range(1, degree+1):
    for j in range(0, i+1):
        data2['F' + str(i-j) + str(j)] = np.power(x1, i-j) * np.power(x2, j)


data2.drop('Test 1', axis=1, inplace=True)
data2.drop('Test 2', axis=1, inplace=True)

print(data2)
 Accepted  Ones       F10  ...           F24           F15           F06

0 1 1 0.051267 … 6.294709e-04 8.589398e-03 1.172060e-01
1 1 1 -0.092742 … 1.893054e-03 -1.398103e-02 1.032560e-01
2 1 1 -0.213710 … 1.048821e-02 -3.397345e-02 1.100469e-01
3 1 1 -0.375000 … 8.944062e-03 -1.197765e-02 1.604015e-02
4 1 1 -0.513250 … 1.238395e-02 -1.123519e-02 1.019299e-02
… … … … … … … …
113 0 1 -0.720620 … 4.374511e-02 -3.270412e-02 2.444980e-02
114 0 1 -0.593890 … 2.115493e-02 -1.762810e-02 1.468924e-02
115 0 1 -0.484450 … 2.340073e-01 -4.826843e-01 9.956280e-01
116 0 1 -0.006336 … 4.003286e-05 -6.313306e-03 9.956280e-01
117 0 1 0.632650 … 3.514745e-07 -1.700678e-08 8.229060e-10

4.正则化代价函数

def costReg(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    reg = (learningRate / (2 * len(X))) * np.sum(np.power(theta[:,1:theta.shape[1]], 2))
    return np.sum(first - second) / len(X) + reg

5.正则化梯度函数

def gradientReg(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:, i])

        if (i == 0):
            grad[i] = np.sum(term) / len(X)
        else:
            grad[i] = (np.sum(term) / len(X)) + ((learningRate / len(X)) * theta[:, i])

    return grad

6.数据集预处理

# 初始化X,y,θ
cols = data2.shape[1]
X2 = data2.iloc[:,1:cols]
y2 = data2.iloc[:,0:1]
theta2 = np.zeros(cols-1)
# 进行类型转换
X2 = np.array(X2.values)
y2 = np.array(y2.values)

#λ设为1

learningRate = 1

costReg(theta2, X2, y2, learningRate)
print(costReg(theta2, X2, y2, learningRate))

0.6931471805599454

7.数据集预处理

result2 = opt.fmin_tnc(func=costReg, x0=theta2, fprime=gradientReg, args=(X2, y2, learningRate))
print(result2)

(array([ 1.27271027, 0.62529965, 1.18111686, -2.01987399, -0.9174319 ,
-1.43166929, 0.12393227, -0.36553118, -0.35725403, -0.17516291,
-1.45817009, -0.05098418, -0.61558554, -0.27469165, -1.19271298,
-0.2421784 , -0.20603299, -0.04466178, -0.2777895 , -0.29539514,
-0.45645981, -1.04319154, 0.02779373, -0.2924487 , 0.0155576 ,
-0.32742405, -0.1438915 , -0.92467487]), 32, 1)
使用第1部分中的预测函数来查方案在训练数据上的准确度:

theta_min = np.matrix(result2[0])
predictions = predict(theta_min, X2)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y2)]
accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))

accuracy = 98%

8.画出决策曲线

def hfunc2(theta, x1, x2):
    temp = theta[0][0]
    place = 0
    for i in range(1, degree+1):
        for j in range(0, i+1):
            temp+= np.power(x1, i-j) * np.power(x2, j) * theta[0][place+1]
            place+=1
    return temp
def find_decision_boundary(theta):
    t1 = np.linspace(-1, 1.5, 1000)
    t2 = np.linspace(-1, 1.5, 1000)

    cordinates = [(x, y) for x in t1 for y in t2]
    x_cord, y_cord = zip(*cordinates)
    h_val = pd.DataFrame({'x1':x_cord, 'x2':y_cord})
    h_val['hval'] = hfunc2(theta, h_val['x1'], h_val['x2'])

    decision = h_val[np.abs(h_val['hval']) < 2 * 10**-3]
    return decision.x1, decision.x2

作图

fig, ax = plt.subplots(figsize=(9,6))
ax.set_title("Decision-making curve 1", fontsize=14)                                  # 图名字,字号14
ax.set_xlabel("Test 1 Score", fontsize=10)                                            # x轴标签,字号10
ax.set_ylabel("Test 2 Score", fontsize=10)                                            # y轴标签,字号10
ax.scatter(positive2['Test 1'], positive2['Test 2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative2['Test 1'], negative2['Test 2'], s=50, c='r', marker='x', label='Rejected')
x, y = find_decision_boundary(result2)
plt.scatter(x, y, c='y', s=50, marker='o',  label='Prediction')
ax.legend(loc='lower left')
plt.show()

在这里插入图片描述

9.λ=0时,决策曲线

learningRate2 = 0
result3 = opt.fmin_tnc(func=costReg, x0=theta2, fprime=gradientReg, args=(X2, y2, learningRate2))
``````c
fig, ax = plt.subplots(figsize=(9,6))
ax.set_title("Decision-making curve 2", fontsize=14)                                  # 图名字,字号14
ax.set_xlabel("Test 1 Score", fontsize=10)                                            # x轴标签,字号10
ax.set_ylabel("Test 2 Score", fontsize=10)                                            # y轴标签,字号10
ax.scatter(positive2['Test 1'], positive2['Test 2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative2['Test 1'], negative2['Test 2'], s=50, c='r', marker='x', label='Rejected')
x, y = find_decision_boundary(result3)
plt.scatter(x, y, c='y', s=50, marker='o',  label='Prediction')
ax.legend(loc='lower left')
plt.show()

在这里插入图片描述

10.λ=100时,决策曲线

learningRate2 = 100
result4 = opt.fmin_tnc(func=costReg, x0=theta2, fprime=gradientReg, args=(X2, y2, learningRate2))
``````c
fig, ax = plt.subplots(figsize=(9,6))
ax.set_title("Decision-making curve 3", fontsize=14)                                  # 图名字,字号14
ax.set_xlabel("Test 1 Score", fontsize=10)                                            # x轴标签,字号10
ax.set_ylabel("Test 2 Score", fontsize=10)                                            # y轴标签,字号10
ax.scatter(positive2['Test 1'], positive2['Test 2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative2['Test 1'], negative2['Test 2'], s=50, c='r', marker='x', label='Rejected')
x, y = find_decision_boundary(result4)
plt.scatter(x, y, c='y', s=50, marker='o',  label='Prediction')
ax.legend(loc='lower left')
plt.show()

在这里插入图片描述

总结

1. Scipy优化算法–scipy.optimize.fmin_tnc()

有约束的多元函数问题,提供梯度信息,使用截断牛顿法。
调用:

scipy.optimize.fmin_tnc(func, x0, fprime=None, args=(), approx_grad=0, bounds=None, epsilon=1e-08, scale=None, offset=None, messages=15, maxCGit=-1, maxfun=None, eta=-1, stepmx=0, accuracy=0, fmin=0, ftol=-1, xtol=-1, pgtol=-1, rescale=-1, disp=None, callback=None)

最常使用的参数:

调用参数解释
func优化的目标函数
x0初值
fprime提供优化函数func的梯度函数,不然优化函数func必须返回函数值和梯度,或者设置approx_grad=True
approx_grad如果设置为True,会给出近似梯度
args元组,是传递给优化函数的参数
返回参数解释
x数组,返回的优化问题目标值
nfeval整数,function evaluations的数目;在进行优化的时候,每当目标优化函数被调用一次,就算一个function evaluation。在一次迭代过程中会有多次function evaluation。这个参数不等同于迭代次数,而往往大于迭代次数。
rcint,Return code, see below

参考:https://docs.scipy.org/doc/scipy-1.2.1/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值