机器学习算法各个击破

课程简介

本课程是面向机器学习小白的算法必修课,注重理论与实战结合,用白话的方式讲解算法中的数学原理,并附带完整的源代码帮助读者进行实践。

同时本课程汇总了作者在机器学习数据挖掘领域的经验和一线互联网公司的面试心得,通过列举常见机器学习面试题及相关答题技巧来帮助读者提高面试成功率。

全部内容共计 12 篇图文课程,支持 PDF 下载和读者圈答疑。

专家推荐

《机器学习算法各个击破》是针对机器学习实战领域的实用性课程。理论深入浅出,实战通俗易懂,使用 Python 作为课程的主打语言,减轻了读者的语言学习成本。各层次读者都能从中获益匪浅,尤其适用于机器学习领域的初学者,甚至是零编程基础的本科在校生。

—— 林沂蒙,微软人工智能应用科学家

作者介绍

章华燕,机器学习硕士毕业,CSDN 博客专家,GitChat 精品课畅销作者,曾就职于微软、京东、阿里巴巴等大型互联网公司。主要负责项目:广告点击反作弊,基于大数据的店铺选址模型预测。目前就职于陌陌,担任推荐算法工程师,负责陌陌直播算法推荐。擅长技术:计算机视觉、机器学习、深度学习、大数据分析与计算、密码学算法。

个人微信:TonyJeemy520,微信公众号:机器学习算法工程师。

课程内容
第01课:机器学习基础知识

引言

人工智能、深度学习、机器学习……相信目前的 IT 互联网从业者对这些名词耳熟能详。人工智能其实早已出现,为什么等到本世纪初才再一次火起来呢?人工智能、深度学习、机器学习三者之间的关系又是怎样的呢?

针对第一个问题,原因可以概括为以下几点。

  • 计算能力的提升:随着计算机软硬件技术的发展,以前的图像处理专用硬件 GPU 开始投入到大规模高度并行单元计算当中,更进一步应用到分布式 GPU 集群的搭建中,这一切使以前几个月都完成不了的模型训练任务在今天可以很快完成。
  • 更加优化的算法:以深度神经网络为基础的算法原理得到进一步改进,无论在应用方向上还是优化精度上,深度学习算法都取得了质的飞跃。
  • 大数据时代的到来:之前互联网数据大多来自于 PC 端,但因数据量较少,算法训练很容易出现过拟合问题。而随着移动互联网时代的到来,各种移动 App 每时每刻都会产生大量数据,从而促进了深度学习此类需要大量数据训练的算法的广泛应用。

接下来回答第二个问题。我们知道,人工智能的领域范围最广,深度学习领域最窄。也就是说,机器学习是人工智能的子领域,而深度学习又是机器学习的一个分支。由此可以得出机器学习在 AI 领域有着重要的地位,这也是学习深度学习要先从机器学习着手的原因。

正所谓 “ 基础不牢,地动山摇 ” 就是讲的这个道理。只要你会 Python ,从市面上买一本 TensorFlow 相关实战图书就能跑出一个实验,体会到深度学习的强大效果和能力。到了这一步,就可以说懂深度学习了吗?显然不是,甚至可以说你还没有入门。

机器学习算法的奥妙之处在于算法调优,如果不懂它背后的原理,遇到新问题就很难做出好的效果。况且书本上的数据集一般为公开数据集,算法的最优参数早已给出,无须学习者再通过理论或实践去探索。但现实工作中遇到的往往都是新问题,这时就需要你对算法有很深入的理解,才能探研出解决方案。

1.1 机器学习中的基本问题

前面我们分析了为什么要从机器学习开始学习。接下来将为大家介绍一些基本概念,为将来学习具体的机器学习算法打好基础。

1. 机器学习的特点

机器学习的特点主要归纳为以下几点:

  • 机器学习算法以数据和特征为基础,是数据驱动的科学;

  • 机器学习的目标是对数据进行预测与分析;

  • 机器学习以模型方法为中心,利用统计学习方法构建模型,并利用构建好的模型对未知的数据进行预测和分类;

  • 机器学习是概率论、统计学、信息论、计算理论、最优化及计算机科学等多领域交叉的学科,因此要想在机器学习上有所成绩,必须要有深厚的数学基础。

2. 机器学习的对象

机器学习中最重要的概念就是特征,而特征是最后需要输入到模型中进行训练的多维数据向量,由各种不同类型的数据(如数字、文本、图像、音频、视频等 )转换而来。该转换过程被称为“特征工程”,是机器学习、数据挖掘领域很重要的一个步骤。特征工程的技巧和方法众多,在这里我就不做详细介绍了,后面会在单独课程中讲解。

3. 机器学习的分类

严格意义上的机器学习算法应该分为四大类,即有监督学习、无监督学习、半监督学习和强化学习。

  • 有监督学习:有监督学习是指进行训练的数据既包含特征向量又包含类别标签信息。也就是说,在训练的时候,我们知道每个样本所示的类别或者在回归算法中的结果。这样,在进行算法训练时可以根据类标对参数进行调整,就好比学习的过程被监督了一样,而不是漫无目的地学习。至此,相信大家都知道监督的意义了,即学习的过程,依靠类标来指导算法参数的调整。

  • 无监督学习:清楚了有监督学习的含义就很容易明白什么是无监督学习。相对于有监督学习,无监督学习中,训练数据集只包含每个数据实例的特征向量而没有其所属的标签结果。因此,无监督学习算法不属于分类算法,而往往被叫做聚类算法,即“特征相似的聚集在一起” 。

  • 半监督学习:半监督学习其实是一种结合有监督学习和无监督学习二者的学习方式,它是近年来的研究热点,主要针对的是样本很难收集或有类标的样本很少,绝大多数样本缺失类标值的情况。这种情况下,我们无法利用有监督学习直接进行训练,但我们又想充分利用类标的信息,半监督学习将是一种介于有监督和无监督的折中方案。

  • 强化学习:强化学习是指智能系统从环境到行为映射的学习,以使奖励信号(强化信号)函数值最大。根据百度百科,强化学习不同于连接主义学习中的监督学习,主要表现在教师信号上。强化学习中,环境提供的强化信号将对产生动作的好坏作出评价(通常为标量信号),而不是告诉强化学习系统 RLS(Reinforcement Learning System)如何产生正确的动作。由于外部环境提供的信息很少,RLS 必须靠自身的经历进行学习。通过这种方式,RLS 在 “ 行动-评价 ” 的环境中获得知识,改进行动方案以适应环境。

4. 机器学习的要素

李航在《 统计学习方法 》中将机器学习的三要素总结为模型、策略和算法,各自含义我们接下来分别解释。

  • 模型:其实就是机器学习训练过程中所要学习的条件概率分布或者决策函数。
  • 策略:指模型训练过程中度量学习好坏的方法,根据这个方法实时调整模型参数,以期望训练出的模型将来对未知的数据具有好的预测准确度。
  • 算法:指具体计算方法。它基于训练数据集,根据学习策略,从假设空间中选择最优模型,最后考虑用什么样的计算方法求解这个最优模型。

在李航的总结之外,我个人认为可以再添加一个要素,那就是:特征。机器学习领域流行着这样一句话:数据和特征决定了机器学习算法的上界,而模型和算法只是逼近这个上界而已。这句话充分说明了一个事实:无论你的机器学习算法模型的识别效果有多么准确,如果没有好的特征,也是白搭。换句话说,数据和特征确定了,算法最好能达到怎样的程度,基本上也就已经确定了。此时,好或坏算法的标准可能就是谁更接近基于这个数据和特征的效果上限。

因此,我将机器学习算法要素总结为特征模型策略算法

1.2 机器学习建模过程

1. 模型的求解

假设输入特征变量为 $X$ ,输出变量为 $Y$ ,它们对应的具体取值分别为 $x$ 和 $y$ ,输入变量 $X$ 可以是标量也可以是向量。除非特殊声明,本课程中所说的特征向量都是指列向量。输入实例 $x$ 的列向量可以表示为:

$$x=(x^{(1)},x^{(1)},...,x^{(i)},...,x^{(n)})^T$$

其中, $x^{(i)}$ 表示 $x$ 的第 $i$ 个特征值,因此 $x$ 是一个具有 $n$ 个特征值的特征向量。注意,我们将使用另一种表示方法,用 $xi$ 表示第 $i$ 个输入实例,第 $ i$ 个输入实例的第 $k$ 个特征值则表示为 $xi^{(k)}$ 。因此,具有 $N$ 个训练实例的有监督学习的训练数据集就可以表示为:

$$T={(x1,y1),(x2,y2),...,(xN,yN)}$$

有了以上数据表示,一个机器学习算法的提出过程基本包括以下步骤:

  • 根据特征向量的数据分布,提出一个合适的模型函数 $y=f(x;\theta)$ 来估计参数分布;

  • 提出一个合适的损失函数 $L(x,y)$ 来计算训练数据集上所有训练样本大概的误差损失:

$$L(x,y)=\frac{1}{N}\sum{i=1}^NL(yi,f(x_i))$$

  • 选取合适的优化算法,使损失函数 $L(x,y)$ 的值最小化,即:

$$\operatorname*{min}{f\in F}\frac{1}{N}\sum{i=1}^NL(yi,f(xi))$$

  • 最优化上述函数值,求解得到 $L(yi,f(xi))$ 的最小值,从而得到原函数 $y=f(x;\theta)$ 的参数值 $\theta$ :

$$\theta = (\theta^{(1)},\theta^{(2)},...,\theta^{(K)})$$

其中,参数个数 $K$ 与模型函数 $f(x;\theta)$ 相关,与特征向量维数及数据集个数无关。这样,新的类标未知的样本 $x$ 直接输入到函数 $f(x)$ 中,就可以得到新的预测类标值 $y$ 。

2. 经验风险与结构风险

上面求解模型函数 $f(x)$ ,使用的是经验风险最小化( Empirical Risk Minimization,ERM )原则。然而,在实际工作中,算法的实现经常使用的是另一种原则,即结构风险最小化( Structral Risk Minimization,SRM )。

  • 经验风险最小化

上面求解出最小化损失函数对应的参数 $\theta$ 的策略,被称为经验风险最小化。该策略认为经验风险最小的模型就是最优的模型,即:

$$\operatorname*{min}{f\in F}\frac{1}{N}\sum{i=1}^NL(yi,f(xi))$$

其中 $F$ 是假设空间。统计学中的极大似然估计( Maximum Likelihood Estimation,MLE )就是经验风险最小化的一个典型例子。当模型为条件概率分布,损失函数是对数损失函数时,经验风险最小化与极大似然估计等价( 参考文末参考文献 [1] )。虽然在样本数量足够大的情况下,经验风险最小化求解出来的模型能取得不错的预测效果,但当训练数据集也就是样本容量比较小时,基于经验风险最小化训练出来的模型往往容易过拟合。

  • 结构风险最小化

与经验风险最小化相对应的是结构风险最小化。结构风险最小化是为了防止过拟合而提出来的一种策略,它与正则化等价。结构风险在经验风险的基础上加上了表示模型复杂度的正则化项或者罚项。其定义如下:

$$R{srm} = \frac{1}{N}\sum{i=1}^NL(yi,f(xi))+\lambda J(f)$$

其中,$J(f)$ 为模型的复杂度,它的值应该随着模型 $f$ 复杂度的增加而增大,这样才能起到抑制模型复杂度的作用。$\lambda\ge0$ 是系数,它的作用是折中经验风险和模型复杂度。实践证明,结构风险小的模型往往对训练数据以及未知的测试数据具有较好的预测效果。

参考文献

[1] 李航. 统计学习方法[M]. 清华大学出版社, 2012.

[2] 周志华. 机器学习 : = Machine learning[M]. 清华大学出版社, 2016.

[help me with MathJax]

点击了解更多《机器学习算法各个击破》

第02课:KNN 算法——不学习我也能预测

前言

KNN(K-Nearest Neighbor)算法是机器学习算法中最基础、最简单的算法之一。它既能用于分类,也能用于回归。本文将详细介绍 KNN 算法在分类和回归两种任务下的使用方法。

KNN 算法的思想非常简单。任意 N 维输入向量对应于特征空间的一个点,输出则为该特征向量所对应的类别标签或者预测值。KNN 算法没有显式的学习过程,是它十分特别的地方。其实际工作原理为利用训练数据对特征向量空间进行划分,并将划分的结果作为最终的算法模型。

KNN 分类算法

KNN 分类算法的分类预测过程十分简单,也比较容易理解。

当需要对输入向量 x 进行预测时,我们只需在训练数据集中寻找 k 个与向量 x 最接近的向量的集合,集合中数据最多的类标便被预测为 x 的类标。

e1204910-3af8-11e8-935b-9d72f9362f36

图1 KNN 分类算法示意图

如上图所示,$w1$、$w2$、$w3$ 分别代表训练集中的三个类别。与 $xu$ 最相近的5(k=5)个点(如图中箭头所指)中,很明显 $w1$ 类标占比最多,因此在 KNN 算法中将 $xu$ 的类别预测为 $w_1$。

基于上述思想,我们看看 KNN 算法的运用方法。

输入: 训练数据集

$T={(x1,y1),(x2,y2),...,(xN,yN)}$

其中:

$x_i \in \mathbf X \subseteq \mathbf R^n$

为 n 维实例特征向量。

$yi \in \mathbf Y ={c1,c2,...,cK}$

为实例的类别,$i=1,2,...,N$。

输出: 预测实例 $x$ 所属类别 $y$。

算法执行步骤:

  1. 根据给定的距离度量方法(一般情况下使用欧氏距离),在训练集 $T$ 中寻找出与 $x$ 最相近的 k 个样本点,并将这 k 个样本点所组成的集合记为 $N_k(x)$;
  2. 根据如下所示的多数投票原则,确定实例 $x$ 所属类别 $y$:

式(5.1)中,$I$ 为指示函数:

在讲解 KNN 算法原理时,我们发现只有当两个因素确定后,KNN 分类算法才能真正运行起来,这两个因素便是算法超参数 K 和模型向量空间的距离度量。

K 值的确定

KNN 算法中只有唯一的一个超参数 K,很明显 K 值的选择对最终算法的预测结果有着至关重要的影响。接下来,我们简单讨论下 K 值的大小对算法结果的影响,以及一般情况下如何选择 K 值。

如果选择的 K 值比较少,相当于使用较小领域中的训练样本对实例进行预测。这时,算法的近似误差(Approximate Error)会减小,因为只有与输入实例相近的训练样本才能对预测结果起作用。但它也有明显的缺点,算法的估计误差会偏大,预测的结果会对近邻点十分敏感,也就是说如果近邻点是噪声点的话,预测就会出错。总之,K 值太小很容易使 KNN 算法产生过拟合。

同理,如果 K 值选的比较大,距离较远的训练样本都会对实例的预测结果产生影响。同时,模型相对比较鲁棒,不会因个别噪声点对最终的预测产生影响。但缺点也十分明显,算法的近似误差会偏大,距离较远的点(与预测实例不相似)同样会对预测产生作用,最终使预测结果产生较大偏差。此时,相当于模型发生欠拟合。

因此,在工程实践中,我们一般采用交叉验证的方式选取 K 值。从上面的分析也可知道,一般 K 值取得比较小。我们会在较小的范围内选取 K 值,同时将测试集上准确率最高的那一个确定为最终的算法超参数 K。

距离度量

样本空间中两个点之间的距离度量表示的是两个样本点之间的相似程度。距离越短,表示相似程度越高,相反距离越大,表示两个样本的相似程度越低。

常用的距离度量方式有:

  1. 闵可夫斯基距离
  2. 欧氏距离
  3. 曼哈顿距离
  4. 切比雪夫距离
  5. 余弦距离

1. 闵可夫斯基距离

闵可夫斯基距离不是一种距离,而是一类距离的定义。比如 n 维空间中有两个点 $x(x1,x2,...,xn)$ 和 $y(y1,y2,...,yn)$,它们之间的闵可夫斯基距离可以表示为:

$$d{xy}=\sqrt[p]{\sum{k=1}^n(xk-yk)^p}$$

其中,p 是一个可变参数:

  • 当 p=1 时,被称为曼哈顿距离;
  • 当 p=2 时,被称为欧氏距离;
  • 当 p=$\infty$ 时,被称为切比雪夫距离。

2. 欧氏距离

由以上说明可知,欧式距离的计算公式为:

$$d{xy}=\sqrt{\sum{k=1}^n(xk-yk)^2}$$

欧式距离(L2 范数)是最易于理解的一种距离计算方法,源自欧式空间中两点间的距离公式,也是最常用的距离度量方式。

3. 曼哈顿距离

由闵可夫斯基距离定义可知,曼哈顿距离的计算公式为:

$$d{xy}=\sum{k=1}^n|xk-yk|$$

KNN 算法的核心 KDTree

通过以上分析,我们知道 KNN 分类算法的思想非常简单,它所采用的就是 K 最近邻多数投票的思想。所以,算法的关键就是在给定距离度量的情况下,如何准确快速地找到预测实例最近的 K 个邻居。

也许绝大多数初学者会说,直接寻找呗,反正 K 一般取值不会特别大。特征空间维度不高并且训练样本容量小的情况下确实可行,但当特征空间维度特别高或者样本容量很大时,计算将非常耗时,该方法便不可行了。

为了快速查找到 K 个近邻,我们可以考虑使用特殊的数据结构存储训练数据,用来减少搜索次数。KDTree 就是其中最著名的一种。

KD 树简介

KD 树(K-dimension Tree)是一种树形数据结构,用来存储 K 维空间中的实例点,并可对其快速检索。

f1105540-3af8-11e8-935b-9d72f9362f36

它是一种二叉树,是对 K 维空间的一个划分。构造 KD 树,相当于不断用垂直于坐标轴的超平面对 K 维空间进行切分,从而构建出一系列的 K 维超矩形区域。KD 树的每个节点分别对应一个 K 维超矩形区域。利用 KD 树可以省去对大部分数据点的搜索,从而减少搜索的计算量。

KD 树的构造

构造 KD 树是一个递归过程,主要包括以下两个步骤:

  1. 首先,构造根节点,使其与 K 维空间所包含的所有点的超矩形区域相对应;
  2. 对 K 维空间不断切分,生成子节点。

1. 构造根节点

首先,在包含所有节点的超矩形区域中选择一个坐标轴,以及此坐标轴上的一个切分点,并确定一个垂直于该坐标轴的超平面。这个超平面将当前区域划分为两个子区域(即二叉树左右的两个孩子节点)。

2. 递归构造子节点

接着,对这两个子区域进行相同的划分,递归向下进行,直到子区域内没有实例时终止(此时只有叶子节点)。

通常,我们循环选择坐标轴对空间进行划分。当选定一个维度坐标时,切分点可选择所有训练实例在该坐标轴上的中位数。此时构造的 KD 树是平衡二叉树,但在搜索时,平衡二叉树不一定是最高效的。

KNN 回归算法

前面我们提到 KNN 算法主要用来分类,实际上,它也能用于回归预测。接下来,我们就介绍一下 KNN 算法是如何进行回归预测的。

众所周知,对于一个新预测实例,KNN 算法对其进行分类的过程是这样的:

  • 首先,在训练集上寻找与它最相近的 K 个近邻;
  • 然后,采用投票法将它分到这 K 个邻居中分类最多的那个类中。

那怎么将 KNN 算法用于回归预测呢?其实大致的步骤是一样的,也是寻找新预测实例的 K 个近邻,然后对这 K 个样本的目标值去均值,即可作为新样本的预测值:

$$\hat y=\frac{1}{K}\sum{i=1}^Kyi$$

KNN 预测实战

接下来,我们通过使用 scikit-learn 库中的 KNN,对 iris 数据集的分类进行预测。

众所周知,iris 数据集有四个维度的特征。为了方便展示效果,我们只使用其中的两个维度。完整的代码实现如下:

import numpy as npimport matplotlib.pyplot as pltfrom matplotlib.colors import ListedColormapfrom sklearn import neighbors, datasetsn_neighbors = 15iris = datasets.load_iris()X = iris.data[:, :2]y = iris.targeth = .02  # step size in the meshcmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])for weights in ['uniform', 'distance']:    clf = neighbors.KNeighborsClassifier(n_neighbors, weights=weights)    clf.fit(X, y)    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),                         np.arange(y_min, y_max, h))    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])    Z = Z.reshape(xx.shape)    plt.figure()    plt.pcolormesh(xx, yy, Z, cmap=cmap_light)    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold,                edgecolor='k', s=20)    plt.xlim(xx.min(), xx.max())    plt.ylim(yy.min(), yy.max())    plt.title("3-Class classification (k = %i, weights = '%s')"              % (n_neighbors, weights))plt.show()

代码运行结果如下图所示:

1e1110b0-3afa-11e8-935b-9d72f9362f36
34b25ea0-3afa-11e8-935b-9d72f9362f36

我们再看上面的代码,邻居数 n_neighbors = 15,只使用 iris 的前两维特征作为分类特征。

权重度量采用了两种方式,即均值(Uniform)和距离(Distance)。均值代表所有的 K 个近邻在分类时重要性选取是一样的,为默认参数,距离是指分类时 K 个邻居中每个邻居所占的权重与它和预测实例之间的距离成反比。

[help me with MathJax]

点击了解更多《机器学习算法各个击破》

第03课:逻辑回归(LR)——算法工程师必考
第04课:Naive Bayes 算法——后验概率学习算法
第05课:决策树算法——IF-THEN 规则的集合
第06课:XGBoost——基于决策树的 Kaggle 夺冠利器
第07课:LightGBM——据说这家伙比 XGBoost 还牛
第08课:无监督聚类——从 K-Means 说起
第09课:Isolation Forest——异常检测的利器
第10课:神经网络——深度学习的基础
第11课:特征降维——PCA 与 LDA
第12课:征战BAT——算法面试杂谈

阅读全文: http://gitbook.cn/gitchat/column/5ac2f0509e924a1dc029dd84

机器学习算法详解▪ 一、线性回归 ◦ 1、代价函数 ◦ 2、梯度下降算法 ◦ 3、均值归一化 ◦ 4、最终运行结果 ◦ 5、使用scikit-learn库中的线性模型实现 ▪ 二、逻辑回归 ◦ 1、代价函数 ◦ 2、梯度 ◦ 3、正则化 ◦ 4、S型函数(即) ◦ 5、映射为多项式 ◦ 6、使用的优化方法 ◦ 7、运行结果 ◦ 8、使用scikit-learn库中的逻辑回归模型实现 ▪ 逻辑回归_手写数字识别_OneVsAll ◦ 1、随机显示100个数字 ◦ 2、OneVsAll ◦ 3、手写数字识别 ◦ 4、预测 ◦ 5、运行结果 ◦ 6、使用scikit-learn库中的逻辑回归模型实现 ▪ 三、BP神经网络 ◦ 1、神经网络model ◦ 2、代价函数 ◦ 3、正则化 ◦ 4、反向传播BP ◦ 5、BP可以求梯度的原因 ◦ 6、梯度检查 ◦ 7、权重的随机初始化 ◦ 8、预测 ◦ 9、输出结果 ▪ 四、SVM支持向量机 ◦ 1、代价函数 ◦ 2、Large Margin ◦ 3、SVM Kernel(核函数) ◦ 4、使用中的模型代码 ◦ 5、运行结果 ▪ 五、K-Means聚类算法 ◦ 1、聚类过程 ◦ 2、目标函数 ◦ 3、聚类中心的选择 ◦ 4、聚类个数K的选择 ◦ 5、应用——图片压缩 ◦ 6、使用scikit-learn库中的线性模型实现聚类 ◦ 7、运行结果 ▪ 六、PCA主成分分析(降维) ◦ 1、用处 ◦ 2、2D-->1D,nD-->kD ◦ 3、主成分分析PCA与线性回归的区别 ◦ 4、PCA降维过程 ◦ 5、数据恢复 ◦ 6、主成分个数的选择(即要降的维度) ◦ 7、使用建议 ◦ 8、运行结果 ◦ 9、使用scikit-learn库中的PCA实现降维 ▪ 七、异常检测 Anomaly Detection ◦ 1、高斯分布(正态分布) ◦ 2、异常检测算法 ◦ 3、评价的好坏,以及的选取 ◦ 4、选择使用什么样的feature(单元高斯分布) ◦ 5、多元高斯分布 ◦ 6、单元和多元高斯分布特点 ◦ 7、程序运行结果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值