机器学习基础算法(sklearn)

本文将根据sklearn文档讨论一下机器学习基础算法的参数和基本使用:
kNN
决策树
逻辑回归
朴素贝叶斯
支持向量机
集成学习
线性回归
kMeans

kNN
优点: 精确度高,对异常值不敏感
缺点: 计算复杂度高,空间复杂度高
参数:

  • n_neighbors:默认为5,kNN的k的值,即选取最近的k个点。
  • weights:默认是uniform,也可以是uniform、distance,或者用户自己定义的函数。
    uniform是均等的权重,就说所有的邻近点的权重都是相等的。
    distance是不均等的权重,距离近的点比距离远的点的影响大。
    用户自定义的函数,接收距离的数组,返回一组维数相同的权重。
  • algorithm:快速k近邻搜索算法,默认参数为auto,可以理解为算法自己决定合适的搜索算法。
    除此之外,用户也可以自己指定搜索算法ball_tree、kd_tree、brute方法进行搜索。
  • leaf_size:默认是30,这个是构造的kd树和ball树的大小。这个值的设置会影响树构建的速度和搜索速度,同样也影响着存储树所需的内存大小。需要根据问题的性质选择最优的大小。
  • p:距离度量公式,参数默认为2,使用欧式距离公式。
    也可以设置为1,使用曼哈顿距离公式进行距离度量。
  • metric(不改):用于距离度量,默认度量是minkowski,也就是p=2的欧氏距离
  • metric_params:距离公式的其他关键参数,默认为None。
  • n_jobs:并行处理设置。默认为1,临近点搜索并行工作数。如果为-1,那么CPU的所有cores都用于并行工作。
from sklearn.neighbors import KNeighborsClassifier
model=KNeighborsClassifier(n_neighbors=3) 
model.fit(X, y)#X:training data,y:target value,通常为like-array(ndarray或者list)或者matrix
predicted= model.predict(x_test)#输入一个like-array,返回一个ndarray数组

决策树
优点: 几乎不需要数据预处理
缺点: 可能出现过拟合,可能不稳定
参数:

  • criterion:特征选择标准,默认是gini,可以设置为entropy。gini是基尼不纯度,entropy是香农熵,ID3算法使用的是entropy,CART算法使用的则是gini。
  • splitter:特征划分点选择标准,默认是best,可以设置为random。每个结点的选择策略。best参数是根据算法选择最佳的切分特征,例如gini、entropy。random随机的在部分划分点中找局部最优的划分点。默认的”best”适合样本量不大的时候,而如果样本数据量非常大,此时决策树构建推荐”random”。
  • max_depth:决策树最大深度,默认是None。如果这个参数设置为None,那么决策树在建立子树的时候不会限制子树的深度。一般来说,数据少或者特征少的时候可以不管这个值。如果设置了min_samples_slipt参数,那么直到少于min_smaples_split个样本为止。
  • min_samples_split:内部节点再划分所需最小样本数,默认是2。这个值限制了子树继续划分的条件。如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。
  • min_weight_fraction_leaf:叶子节点最小的样本权重和,默认是0。这个值限制了叶子节点所有样本权重和的最小值,如果小于这个值,则会和兄弟节点一起被剪枝。一般来说,如果我们有较多样本有缺失值,或者分类树样本的分布类别偏差很大,会引入样本权重。
  • max_features:划分时考虑的最大特征数,默认是None。寻找最佳切分时考虑的最大特征数(n_features为总共的特征数),有如下6种情况:
    • 如果max_features是整型的数,则考虑max_features个特征;
    • 如果max_features是浮点型的数,则考虑int(max_features * n_features)个特征;
    • 如果max_features设为auto,那么max_features = sqrt(n_features);
    • 如果max_features设为sqrt,那么max_featrues = sqrt(n_features),跟auto一样;
    • 如果max_features设为log2,那么max_features = log2(n_features);
    • 如果max_features设为None,那么max_features = n_features,也就是所有特征都用。
      一般来说,如果样本特征数不多,比如小于50,我们用默认的”None”就可以了,如果特征数非常多,可以使用其他取值来控制划分时考虑的最大特征数,以控制决策树的生成时间。
  • max_leaf_nodes:最大叶子节点数,可选参数,默认是None。通过限制最大叶子节点数,可以防止过拟合。如果加了限制,算法会建立在最大叶子节点数内最优的决策树。如果特征不多,可以不考虑这个值。
  • min_impurity_split:节点划分最小不纯度,可选参数,默认是1e-7。这是个阈值,这个值限制了决策树的增长,如果某节点的不纯度(基尼系数,信息增益,均方差,绝对差)小于这个阈值,则该节点不再生成子节点。即为叶子节点。
  • class_weight:类别权重,可选参数,默认是None,也可以字典、字典列表、balanced。指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。类别的权重可以通过{class_label:weight}这样的格式给出,这里可以自己指定各个样本的权重,或者用balanced,如果使用balanced,则算法会自己计算权重,样本量少的类别所对应的样本权重会高。
from sklearn.tree import DecisionTreeClassifier
model = tree.DecisionTreeClassifier() 
model.fit(X, y)
predicted=model.predict(data)#返回ndarray数组

逻辑回归
优点: 简单
缺点: 容易欠拟合,分类精度不高
参数:

  • penalty:惩罚项,str类型,可选参数为l1和l2,默认为l2。L1假设的是模型的参数满足拉普拉斯分布,L2假设的模型参数满足高斯分布。
  • dual:对偶或原始方法,bool类型,默认为False。对偶方法只用在求解线性多核(liblinear)的L2惩罚项上。当样本数量>样本特征的时候,dual通常设置为False。
  • tol:停止求解的标准,float类型,默认为1e-4。就是求解到多少的时候,停止,认为已经求出最优解。
  • c:正则化系数λ的倒数,float类型,默认为1.0。必须是正浮点型数。像SVM一样,越小的数值表示越强的正则化。
  • fit_intercept:是否存在截距或偏差,bool类型,默认为True。
  • class_weight:用于标示分类模型中各种类型的权重,可以是一个字典或者balanced字符串,默认为None。如果选择输入的话,可以选择balanced让类库自己计算类型权重,或者自己输入各个类型的权重。举个例子,比如对于0,1的二元模型,我们可以定义class_weight={0:0.9,1:0.1},这样类型0的权重为90%,而类型1的权重为10%。如果class_weight选择balanced,那么类库会根据训练样本量来计算权重。某种类型样本量越多,则权重越低,样本量越少,则权重越高。
    class_weight作用:
    • 误分类的代价很高。比如对合法用户和非法用户进行分类,将非法用户分类为合法用户的代价很高,我们宁愿将合法用户分类为非法用户,这时可以人工再甄别,但是却不愿将非法用户分类为合法用户。这时,我们可以适当提高非法用户的权重。
    • 样本是高度失衡的,比如我们有合法用户和非法用户的二元样本数据10000条,里面合法用户有9995条,非法用户只有5条,如果我们不考虑权重,则我们可以将所有的测试集都预测为合法用户,这样预测准确率理论上有99.95%,但是却没有任何意义。
      这时,我们可以选择balanced,让类库自动提高非法用户样本的权重。提高了某种分类的权重,相比不考虑权重,会有更多的样本分类划分到高权重的类别,从而可以解决上面两类问题。
  • solver:优化算法选择参数,只有五个可选参数,即newton-cg,lbfgs,liblinear,sag,saga。默认为liblinear。solver参数决定了我们对逻辑回归损失函数的优化方法,分别是:
    • liblinear:使用了开源的liblinear库实现,内部使用了坐标轴下降法来迭代优化损失函数。
    • lbfgs:拟牛顿法的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
    • newton-cg:也是牛顿法家族的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
    • sag:即随机平均梯度下降,是梯度下降法的变种,和普通梯度下降法的区别是每次迭代仅仅用一部分的样本来计算梯度,适合于样本数据多的时候。
    • saga:线性收敛的随机优化算法的的变重。
    • 总结:
      liblinear适用于小数据集,而sag和saga适用于大数据集因为速度更快。
      对于多分类问题,只有lbfgs,newton-cg,sag,saga能够处理多项损失,而liblinear受限于一对剩余(OvR)。就是用liblinear的时候,如果是多分类问题,得先把一种类别作为一个类别,剩余的所有类别作为另外一个类别。一次类推,遍历所有类别,进行分类。
      newton-cg,sag和lbfgs这三种优化算法时都需要损失函数的一阶或者二阶连续导数,因此不能用于没有连续导数的L1正则化,只能用于L2正则化。而liblinear和saga通吃L1正则化和L2正则化。
      同时,sag每次仅仅使用了部分样本进行梯度迭代,所以当样本量少的时候不要选择它,而如果样本量非常大,比如大于10万,sag是第一选择。但是sag不能用于L1正则化,所以当你有大量的样本,又需要L1正则化的话就要自己做取舍了。要么通过对样本采样来降低样本量,要么回到L2正则化。
      当我们需要相对精确的多元逻辑回归时,就不能选择liblinear了。也意味着如果我们需要相对精确的多元逻辑回归不能使用L1正则化了。
  • max_iter:算法收敛最大迭代次数,int类型,默认为100。仅在正则化优化算法为newton-cg, sag和lbfgs才有用,算法收敛的最大迭代次数。
  • multi_class:分类方式选择参数,str类型,可选参数为ovr和multinomial和auto,默认为auto。ovr即前面提到的one-vs-rest(OvR),而multinomial即前面提到的many-vs-many(MvM)。如果是二元逻辑回归,ovr和multinomial并没有任何区别,区别主要在多元逻辑回归上。
    OvR和MvM有什么不同?
    OvR的思想很简单,无论你是多少元逻辑回归,我们都可以看做二元逻辑回归。具体做法是,对于第K类的分类决策,我们把所有第K类的样本作为正例,除了第K类样本以外的所有样本都作为负例,然后在上面做二元逻辑回归,得到第K类的分类模型。其他类的分类模型获得以此类推。
    而MvM则相对复杂,这里举MvM的特例one-vs-one(OvO)作讲解。如果模型有T类,我们每次在所有的T类样本里面选择两类样本出来,不妨记为T1类和T2类,把所有的输出为T1和T2的样本放在一起,把T1作为正例,T2作为负例,进行二元逻辑回归,得到模型参数。我们一共需要T(T-1)/2次分类。
    可以看出OvR相对简单,但分类效果相对略差(这里指大多数样本分布情况,某些样本分布下OvR可能更好)。而MvM分类相对精确,但是分类速度没有OvR快。如果选择了ovr,则4种损失函数的优化方法liblinear,newton-cg,lbfgs和sag都可以选择。但是如果选择了multinomial,则只能选择newton-cg, lbfgs和sag了。
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
X, y = load_iris(return_X_y=True)
clf = LogisticRegression(random_state=0).fit(X, y)
clf.predict(X[:2, :])#返回adarray类型
clf.score(X, y)#返回平均准确度

朴素贝叶斯
优点: 数据量少的情况下依然有效,可处理多分类问题
缺点: 对输入数据样式敏感
sklearn中,一共有3个朴素贝叶斯的分类算法类。分别是GaussianNB,MultinomialNB和BernoulliNB。其中GaussianNB就是先验为高斯分布的朴素贝叶斯,MultinomialNB就是先验为多项式分布的朴素贝叶斯,而BernoulliNB就是先验为伯努利分布的朴素贝叶斯。一般使用先验概率为多项式分布的朴素贝叶斯。
参数:

  • alpha:浮点型可选参数,默认为1.0,其实就是添加拉普拉斯平滑,即为上述公式中的λ ,如果这个参数设置为0,就是不添加平滑;
  • fit_prior:布尔型可选参数,默认为True。布尔参数fit_prior表示是否要考虑先验概率,如果是false,则所有的样本类别输出都有相同的类别先验概率。否则可以自己用第三个参数class_prior输入先验概率,或者不输入第三个参数class_prior让MultinomialNB自己从训练集样本来计算先验概率。
  • class_prior:可选参数,默认为None。
from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB()
clf.fit(X, y)
clf.predict(X[2:3])

支持向量机(SVM)
优点: 错误率低,最好的分类器
缺点: 对参数调节和核函数的选择敏感
参数:

  • C:惩罚项,float类型,可选参数,默认为1.0,C越大,即对分错样本的惩罚程度越大,因此在训练样本中准确率越高,但是泛化能力降低,也就是对测试数据的分类准确率降低。相反,减小C的话,容许训练样本中有一些误分类错误样本,泛化能力强。对于训练样本带有噪声的情况,一般采用后者,把训练样本集中错误分类的样本作为噪声。
  • kernel:核函数类型,str类型,默认为’rbf’。可选参数为:
    • ’linear’:线性核函数
    • ‘poly’:多项式核函数
    • ‘rbf’:径像核函数/高斯核
    • ‘sigmod’:sigmod核函数
    • ‘precomputed’:核矩阵,自己提前计算好核函数矩阵,这时候算法内部就不再用核函数去计算核矩阵,而是直接用你给的核矩阵,核矩阵需要为n*n的。
  • degree:多项式核函数的阶数,int类型,可选参数,默认为3。这个参数只对多项式核函数有用。
  • gamma:核函数系数,float类型,可选参数,默认为auto。只对’rbf’ ,’poly’ ,’sigmod’有效。
  • coef0:核函数中的独立项,float类型,可选参数,默认为0.0。只有对’poly’ 和,’sigmod’核函数有用,是指其中的参数c。
  • probability:是否启用概率估计,bool类型,可选参数,默认为False,这必须在调用fit()之前启用,并且会fit()方法速度变慢。
    shrinking:是否采用启发式收缩方式,bool类型,可选参数,默认为True。
  • tol:svm停止训练的误差精度,float类型,可选参数,默认为1e^-3。
  • cache_size:内存大小,float类型,可选参数,默认为200。指定训练所需要的内存,以MB为单位,默认为200MB。
  • class_weight:类别权重,dict类型或str类型,可选参数,默认为None。给每个类别分别设置不同的惩罚参数C,如果没有给,则会给所有类别都给C=1,即前面参数指出的参数C。如果给定参数’balance’,则使用y的值自动调整与输入数据中的类频率成反比的权重。
  • max_iter:最大迭代次数,int类型,默认为-1,表示不限制。
  • decision_function_shape:决策函数类型,可选参数’ovo’和’ovr’,默认为’ovr’。’ovo’表示one vs one,’ovr’表示one vs rest。
from sklearn.svm import SVC
clf = SVC()
clf.fit(X, y)
clf.predict([[-0.8, -1]])

Gradient Boosting
参数

  • loss:指的是每一次节点分裂所要最小化的损失函数,可选deviance和exponential,默认为deviance
  • learning_ rate:默认为1,这个参数决定着每一个决定树对于最终结果的影响。GBM设定了初始的权重值之后,每一次树分类都会更新这个值,而learning_ rate控制着每次更新的幅度。一般来说这个值不应该设的比较大,因为较小的learning rate使得模型对不同的树更加稳健,就能更好地综合它们的结果。
  • n_ estimators:默认100,定义了需要使用到的决定树的数量,虽然GBM即使在有较多决定树时仍然能保持稳健,但还是可能发生过度拟合。所以需要针对learning rate用CV值检验。
  • subsample:默认1,训练每个决定树所用到的子样本占总样本的比例,而对于子样本的选择是随机的。
  • init:它影响了输出参数的起始化过程,默认为none,如果我们有一个模型,它的输出结果会用来作为GBM模型的起始估计,这个时候就可以用init
  • random_ state:作为每次产生随机数的随机种子,可选int或者RandomState,默认为none,使用随机种子对于调参过程是很重要的,因为如果我们每次都用不同的随机种子,即使参数值没变每次出来的结果也会不同,这样不利于比较不同模型的结果。
  • verbose:决定建模完成后对输出的打印方式:
    0:不输出任何结果(默认)
    1:打印特定区域的树的输出结果
    1:打印所有结果
  • warm_ start:默认为False,使用它我们就可以用一个建好的模型来训练额外的决定树,能节省大量的时间。
    树参数:
  • min_ samples_split:定义了树中一个节点所需要用来分裂的最少样本数。可以避免过度拟合。如果用于分类的样本数太小,模型可能只适用于用来训练的样本的分类,而用较多的样本数则可以避免这个问题。但是如果设定的值过大,就可能出现欠拟合现象。因此我们可以用CV值考量调节效果。
  • min_ samples_leaf:定义了树中终点节点所需要的最少的样本数,它也可以用来防止过度拟合。在不均等分类问题中(imbalanced class problems),一般这个参数需要被设定为较小的值,因为大部分少数类别含有的样本都比较小。
  • min_ weight_ fraction_leaf:和上面min_ samples_ leaf很像,不同的是这里需要的是一个比例而不是绝对数值:终点节点所需的样本数占总样本数的比值。
  • max_ depth:定义了树的最大深度。它也可以控制过度拟合,因为分类树越深就越可能过度拟合,也应该用CV值检验。
  • max_ leaf_ nodes:定义了决定树里最多能有多少个终点节点。
    这个属性有可能在上面max_ depth里就被定义了。比如深度为n的二叉树就有最多2^n个终点节点。
    如果我们定义了max_ leaf_ nodes,GBM就会忽略前面的max_depth。
  • max_ features:决定了用于分类的特征数,是人为随机定义的。
    根据经验一般选择总特征数的平方根就可以工作得很好了,但还是应该用不同的值尝试,最多可以尝试总特征数的30%-40%.
    过多的分类特征可能也会导致过度拟合。
from sklearn.ensemble import GradientBoostingClassifier
model= GradientBoostingClassifier(n_estimators=100, learning_rate=1.0, max_depth=1, random_state=0)
model.fit(X, y)
predicted= model.predict(x_test)

随机森林
参数:

  • criterion:特征选择标准,默认是gini,可以设置为entropy。gini是基尼不纯度,entropy是香农熵,ID3算法使用的是entropy,CART算法使用的则是gini。
  • n_ estimators:默认100,定义了需要使用到的决定树的数量,虽然GBM即使在有较多决定树时仍然能保持稳健,但还是可能发生过度拟合。所以需要针对learning rate用CV值检验。
  • random_ state:作为每次产生随机数的随机种子,可选int或者RandomState,默认为none,使用随机种子对于调参过程是很重要的,因为如果我们每次都用不同的随机种子,即使参数值没变每次出来的结果也会不同,这样不利于比较不同模型的结果。
  • verbose:决定建模完成后对输出的打印方式:
    0:不输出任何结果(默认)
    1:打印特定区域的树的输出结果
    1:打印所有结果
  • warm_ start:默认为False,使用它我们就可以用一个建好的模型来训练额外的决定树,能节省大量的时间。
  • min_ samples_split:定义了树中一个节点所需要用来分裂的最少样本数。可以避免过度拟合。如果用于分类的样本数太小,模型可能只适用于用来训练的样本的分类,而用较多的样本数则可以避免这个问题。但是如果设定的值过大,就可能出现欠拟合现象。因此我们可以用CV值考量调节效果。
  • min_ samples_leaf:定义了树中终点节点所需要的最少的样本数,它也可以用来防止过度拟合。在不均等分类问题中(imbalanced class problems),一般这个参数需要被设定为较小的值,因为大部分少数类别含有的样本都比较小。
  • min_ weight_ fraction_leaf:和上面min_ samples_ leaf很像,不同的是这里需要的是一个比例而不是绝对数值:终点节点所需的样本数占总样本数的比值。
  • max_ depth:定义了树的最大深度。它也可以控制过度拟合,因为分类树越深就越可能过度拟合,也应该用CV值检验。
  • max_ leaf_ nodes:定义了决定树里最多能有多少个终点节点。
    这个属性有可能在上面max_ depth里就被定义了。比如深度为n的二叉树就有最多2^n个终点节点。
    如果我们定义了max_ leaf_ nodes,GBM就会忽略前面的max_depth。
  • max_ features:决定了用于分类的特征数,是人为随机定义的。
    根据经验一般选择总特征数的平方根就可以工作得很好了,但还是应该用不同的值尝试,最多可以尝试总特征数的30%-40%.
    过多的分类特征可能也会导致过度拟合。
  • class_weight:类别权重,可选参数,默认是None,也可以字典、字典列表、balanced。指定样本各类别的的权重,主要是为了防止训练集某些类别的样本过多,导致训练的决策树过于偏向这些类别。
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(max_depth=2, random_state=0)
clf.fit(X, y)
print(clf.predict(x_test)

线性回归
优点: 简单
缺点: 非线性拟合不好
参数:

  • fit_intercept:是否需要截距,bool类型,默认为True。也就是是否求解b。
  • copt_X:是否覆盖X,默认为true
    岭回归参数:
  • alpha:正则化系数,float类型,默认为1.0。正则化改善了问题的条件并减少了估计的方差。较大的值指定较强的正则化。
  • fit_intercept:是否需要截距,bool类型,默认为True。也就是是否求解b。
  • normalize:是否先进行归一化,bool类型,默认为False。如果为真,则回归X将在回归之前被归一化。 当fit_intercept设置为False时,将忽略此参数。 当回归量归一化时,注意到这使得超参数学习更加鲁棒,并且几乎不依赖于样本的数量。 相同的属性对标准化数据无效。然而,如果你想标准化,请在调用normalize = False训练估计器之前,使用preprocessing.StandardScaler处理数据。
  • copy_X:是否复制X数组,bool类型,默认为True,如果为True,将复制X数组; 否则,它覆盖原数组X。
  • max_iter:最大的迭代次数,int类型,默认为None,最大的迭代次数,对于sparse_cg和lsqr而言,默认次数取决于scipy.sparse.linalg,对于sag而言,则默认为1000次。
  • tol:精度,float类型,默认为0.001。就是解的精度。
  • solver:求解方法,str类型,默认为auto。可选参数为:auto、svd、cholesky、lsqr、sparse_cg、sag。
    • auto根据数据类型自动选择求解器。
    • svd使用X的奇异值分解来计算Ridge系数。对于奇异矩阵比cholesky更稳定。
    • cholesky使用标准的scipy.linalg.solve函数来获得闭合形式的解。
    • sparse_cg使用在scipy.sparse.linalg.cg中找到的共轭梯度求解器。作为迭代算法,这个求解器比大规模数据(设置tol和max_iter的可能性)的cholesky更合适。
    • lsqr使用专用的正则化最小二乘常数scipy.sparse.linalg.lsqr。它是最快的,但可能在旧的scipy版本不可用。它是使用迭代过程。
    • sag使用随机平均梯度下降。它也使用迭代过程,并且当n_samples和n_feature都很大时,通常比其他求解器更快。注意,sag快速收敛仅在具有近似相同尺度的特征上被保证。您可以使用sklearn.preprocessing的缩放器预处理数据。
from sklearn.linear_model import LinearRegression
import numpy as np
X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
y = np.dot(X, np.array([1, 2])) + 3
reg = LinearRegression().fit(X, y)
reg.predict(np.array([[3, 5]]))
from sklearn.linear_model import Ridge
import numpy as np
n_samples, n_features = 10, 5
rng = np.random.RandomState(0)
y = rng.randn(n_samples)
X = rng.randn(n_samples, n_features)
clf = Ridge(alpha=1.0)
clf.fit(X, y)
clf.predict(test)

kMeans
优点: 易于实现
缺点: 可能收敛于局部最小值
参数:

  • n_clusters:簇数,默认为8。
  • inti:初始化方法,可选参数k-means ++,random,默认为k-means ++
    k-means ++:用聪明的方法??为聚类选择初始聚类中心,以加快收敛速度​​。
    random:从初始质心的数据中随机选择k个观测值。
  • max_iter int,默认为300,单次运行的k均值算法的最大迭代次数。
  • tol:默认= 1e-4
  • precompute_distances:是否预计算距离,可选auto或bool,默认auto。
    • auto:如果样本数*聚类数> 1200万,则不预先计算距离。
    • True:始终预先计算距离。
    • False:永远不要预先计算距离。
  • algorithm:可选auto,full, elkan,默认auto。auto为密集数据选择elkan,为稀疏数据选择full。
from sklearn.cluster import KMeans
import numpy as np
X = np.array([[1, 2], [1, 4], [1, 0],
              [10, 2], [10, 4], [10, 0]])
kmeans = KMeans(n_clusters=2).fit(X)
kmeans.predict([[0, 0], [12, 3]])
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值