模型评估与选择

模型评估与选择

2.1 经验误差与过拟合
  1. 把分类错误的样本数占样本总数的比例称为 “错误率”

  2. 精度 = 1-错误率

  3. 实际预测输出与样本的真实输出之间的差异称为 “误差”

  4. 训练集上的误差称为**“训练误差”**

    新样本上的误差称为**“泛化误差”**

  5. 我们希望得到泛化误差小的学习器,然而,我们实际能做的是努力使经验误差最小化,但这种经验误差很小的学习器在大多数情况下都不好。

  6. 当学习器把训练样本学得“太好”的时候,很可能已经把训练样本自身的一些特点当作了所有潜在样本都会具有的一般性质,导致泛化性能下降。这种现象在机器学习中称为**“过拟合”**。

  7. 过拟合是机器学习面临的关键障碍。

    过拟合是无法避免的,我们只能是减小其风险。

2.2 评估方法

​ 训练样本相当于给同学们练习的习题,测试过程则相当与考试,显然,若测试样本被用作训练了,则得到的将是过于“乐观”的估计结果。

2.2.1 留出法
  1. 直接将数据集D划分为两个互斥的集合,其中一个集合作为训练集S,另一个作为测试集T。

  2. 训练/测试集的划分要尽可能保持数据分布的一致性,避免因数据划分过程引入额外的偏差而对最终结果产生影响。

    例如,在分类任务中,至少要保持样本的类别比例相似。分层采样。

  3. 在使用留出法时,一般要采用若干次随机划分、重复进行实验评估后取平均值作为留出法的评估结果。

  4. 此外,我们希望评估的是用D训练出的模型的性能,但留出法需要划分训练集测试集,这会导致一个窘境:若令训练集S包含绝大多数样本,则训练出的模型可能更接近于用D训练出的模型,但由于T比较小,评估结果可能不够稳定准确;若令测试集T多包含一些样本,则训练集S于D差别更大了,被评估的模型与用D训练出的模型相比可能有较大差别,从而降低了评估结果的保真性。

  5. 常见做法是将大约2/3~4/5的样本用于训练,剩余样本用于测试。

2.2.2 交叉验证法
  1. 先将数据集D划分为k个大小相似的互斥子集,每个子集 $ D_i 都 尽 可 能 保 持 数 据 分 布 的 一 致 性 , 即 从 D 中 通 过 分 层 采 样 得 到 。 然 后 , 每 次 用 都尽可能保持数据分布的一致性,即从D中通过分层采样得到。然后,每次用 Dk-1$个子集的并集作为训练集,余下的那个子集作为测试集。这样可得到k组训练/测试集,从而可进行k次训练和测试,最终返回的是这k个测试结果的均值。

  2. 交叉验证法评估结果的稳定性和保真性在很大程度上取决于k的取值,为强调这一点,通常把交叉验证法称为“k折交叉验证”。k最常用的取值是10。

  3. 与留出法相似,将数据集D划分为k个子集同样存在多种划分方式。为减少因样本划分不同而引入的差别,k折交叉验证通常要随机使用不同的划分重复p次,最终的评估结果是这p次k折交叉验证结果的均值,例如常见的有“10次10折交叉验证”。

  4. 假定数据集D中包含m个样本,若令k=m,则得到了交叉验证法的一个特例:留一法(Leave-One-Out,LOO)。

    留一法不受随机样本划分方式的影响,留一法中被实际评估的模型与期望评估的用D训练出的模型很相似,因此,留一法的评估结果往往被认为比较准确。

  5. 留一法也有缺点,在数据集比较大时,训练m个模型的计算开销可能是难以忍受的,而这还是在未考虑算法调参的情况下。另外,留一法的结果也未必永远比其他评估方法准确;“没有免费的午餐”定理对实验评估方法同样适用。

2.2.3 自助法
  1. 给定包含m个样本的数据集D,我们对它进行采样产生数据集 D ‘ D^` D :每次随机从D中挑选一个样本,将其拷贝放入 D ‘ D^` D,然后再将该样本放回初始数据集D中,使得该样本在下次采样时仍有可能被采到;这个过程重复执行m次后,我们就得到了包含m个样本的数据集 D ‘ D^` D,这就是自助采样的结果。

    显然,D中有一部分样本会在 D ‘ D^` D中多次出现,而另一部分样本不出现。

    可以做一个简单的估计,样本在m次采样中始终不被采到的概论是 ( 1 − 1 m ) m {\left(1 - {1 \over m}\right)}^m (1m1)m,取极限得到
    lim ⁡ m → ∞ ( 1 − 1 m ) m → 1 e ≈ 0.368 \begin{aligned} \lim_{m\to \infty} {\left(1 - {1 \over m}\right)}^m \rightarrow {1\over e} \approx 0.368 \end{aligned} mlim(1m1)me10.368
    即通过自助采样,初始数据集D中约有 36.8 % 36.8\% 36.8%的样本未出现在采样数据集 D ‘ D^` D中。于是我们可将 D ‘ D^` D用用作数据集,D\ D ‘ D^` D用作测试集,、;这样,实际评估的模型与期望评估的模型都使用m个训练样本,而我们仍有数据总量约1/3的、没在训练集中出现的样本用于测试。这样的测试结果,称为“包外估计”。

  2. 自助法在数据集较小、难以有效划分训练/测试集时很有用。然而,自助法产生的数据集改变了初始数据集的分布,从而引入估计偏差。因此,在初始数据量足够时,留出法和交叉验证法更常用一些。

2.2.4 调参与最终模型
  1. 参数调得好不好往往对最终模型性能有关键性的影响。

  2. 机器学习常涉及两类参数,

    • 算法的参数,亦称“超参数”,数目常在10以内。

    • 模型的参数,数目可能很多,例如大型”深度学习“模型甚至有上百亿个参数。

    两种调参方式相似,均是产生多个模型之后基于某种估计方法来进行选择;不同之处在于前者通常是由人工设定多个参数候选值后产生模型,后者则是通过学习来产生多个候选模型。

  3. 在研究对比不同算法的泛化性能时,我们用测试集上的判别结果来估计模型在实际使用时的泛化能力,而把训练数据另外划分为训练集和验证集,基于验证集上的性能来进行模型选择和调参。

2.3 性能度量

​ 衡量模型泛化能力的评价标准,就是性能度量。性能度量反映了任务需求,模型的好坏是相对的,什么样的模型是好的,不仅仅取决于算法和数据,还决定于任务需求。

​ 回归任务最常用的性能度量是”均方误差“
E ( f : D ) = 1 m ∑ i = 1 m ( f ( x i ) − y i ) 2 E(f:D) = {1 \over m}{\sum_{i=1}^m{(f(x_i)-y_i)}^2} E(f:D)=m1i=1m(f(xi)yi)2
​ 下面主要介绍分类任务中常用的性能度量。

2.3.1 错误率与精度

分类任务中最常用的两种性能度量,既适用于二分类任务,也适用于多分类任务。

错误率是分类错误的样本数占样本总数的比例。

精度是分类正确的样本数占样本总数的比例。

2.3.2 查准率、查全率与F1

错误率与精度虽常用,但并不能满足所有任务需求。

真实情况预测正例预测反例
正例TP(真正例)FN(假反例)
反例FP(假正例)TN(真反例)

查准率P定义
P = T P T P + F P P = {TP \over {TP+FP}} P=TP+FPTP
查全率R定义
R = T P T P + F N R = {TP \over {TP + FN}} R=TP+FNTP
查准率和查全率是一对矛盾的度量。一般来说,查准率高时,查全率往往偏低;而查全率高时,查准率往往偏低。

如果我们要提高查准率,阈值就会提高,预测正例就会变少,预测的真正例也会变少,导致查全率变低。

例如,若希望将好瓜尽可能多地选出来,则可通过增加选瓜的数量来实现,如果将所有西瓜都选上,那么所有的好瓜也必然都被选上了,但这样查准率就会比较低;若希望选出的瓜中好瓜比例尽可能高,则可只挑选最有把握的瓜,但这样就难免会漏掉不少好瓜,使得查全率较低。通常只有在一些简单的任务中,才可能使查全率和查准率都很高。

在很多情形下,我们可根据学习器的预测结果对样例进行排序,排在前面的是学习器认为“最可能”是正例的样本,排在最后的是学习器认为“最不可能”是正例的样本。按此顺序设置不同的阈值,逐个把样本作为正例进行预测,则每次可以计算出当前的查准率、查全率。

以查准率为纵轴,查全率为横轴作图,得到P-R曲线

若一个学习器的P-R曲线被另一个学习器的曲线完全包住,则可断言后者的性能由于前者。

如果曲线发生了交叉,可以比较P-R曲线下面积的大小,但这个值不太容易估算,就出现了”平衡点“。

”平衡点“(Break-Even Point,BEP)这一性能度量,是”查准率=查全率“时的取值。

但BEP还是过于简化,更常用的是F1度量:
F 1 = 2 × P × R P + R = 2 × T P 样 例 总 数 + T P − T N F1 = {2 \times P \times R \over{P+R}}={2 \times TP \over{样例总数+TP-TN}} F1=P+R2×P×R=+TPTN2×TP
F1是基于查准率与查全率的调和平均定义的:
1 F 1 = 1 2 ( 1 P + 1 R ) {1 \over F1} = {1 \over 2} {({1 \over P} +{1 \over R})} F11=21(P1+R1)
F β F_\beta Fβ则是加权调和平均:
1 F β = 1 1 + β 2 ( 1 p + β 2 R ) {1 \over F_\beta} = {1 \over {1+\beta^2}}{({1\over p} + {\beta^2 \over R})} Fβ1=1+β21(p1+Rβ2)
与算数平均 ( P + R 2 ) ({{P+R}\over{2}}) 2P+R和几何平均 ( P × R ) ({\sqrt{P\times R}}) (P×R )相比,调和平均值更重视较小值。

其中, β > 0 \beta > 0 β>0度量了查全率对查准率的相对重要性。

$ \beta = 1 时 退 化 为 标 准 的 F 1 ; 时退化为标准的F1; 退F1 \beta > 1 时 查 全 率 有 更 大 影 响 ; 时查全率有更大影响; \beta <1$时查准率有更大影响

很多时候我们有多个二分类混淆矩阵,例如进行多次训练/测试,每次得到一个混淆矩阵。希望在n个二分类混淆矩阵上综合考察查准率和查全率。

有两种方法:

  • 先在各混淆矩阵上分别计算出查准率和查全率,再计算平均值,得到”宏查准率“、”宏查全率“和”宏F1“。。
  • 先将各混淆元素的对应元素进行平均,得到TP、FP、TN、FN的平均值,再基于这些平均值计算出”微查准率“、”微查全率“和”微F1“。
2.3.3 ROC与AUC

如上面一节提到的查准率-查全率,排序本身质量的好坏,体现了综合考虑学习器在不同任务下的”期望泛化能力性能“的好坏。ROC曲线则是从这个角度出发来研究学习器泛化性能的有利工具。

ROC曲线的纵轴是”真正例率“(True Positive Rate,PTR),横轴是”假正例率“(False Positive Rate,FPR)
T P R = T P T P + F N TPR = {TP \over {TP +FN}} TPR=TP+FNTP

F P R = F P T N + F P FPR = {FP \over {TN+FP}} FPR=TN+FPFP

现实任务中通常是利用有限个测试样例来绘制ROC图,此时仅能获得有限个坐标对,无法产生光滑ROC曲线,只能绘制近似ROC曲线。

绘图过程:给定 m + m^+ m+个正例个 m − m^- m个反例,根据学习器预测结果对样例进行排序,然后把分类阈值设为最大,即所有样例均预测为反例,此时TPR和FPR均为0,在坐标(0,0)绘制一个点。然后,将分类阈值依次设为每个样例的预测值,依次将每个样例划分为正例。设前一个标记点坐标为(x,y),当前若为真正例,则对应标记点的坐标为 ( x , y + 1 m + ) (x,y+{1\over{m^+}}) x,y+m+1;当前若为假正例,则对应标记点的坐标为 ( x + 1 m − , y ) (x+{1 \over m^-},y) x+m1,y,然后用线段连接相邻点即可。

进行机器学习比较时,与P-R图相似,若一个学习器的ROC曲线被另一个学习器的曲线完全包住,则可断言后者的性能优于前者。

若发生交叉,比较ROC曲线下的面积,即AUC(Area Under ROC Curve)。

假设ROC曲线是由坐标 { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) } \{(x_1,y_1),(x_2, y_2),...,(x_m,y_m) \} {x1,y1,(x2,y2),...,(xm,ym)}的点按序连接而形成( x 1 = 0 , x m = 1 x_1 = 0, x_m = 1 x1=0,xm=1),则AUC可估算为
A U C = 1 2 ∑ i = 1 m − 1 ( x i + 1 − x i ) ( y i + y i + 1 ) AUC = {1\over 2}{\sum_{i=1}^{m-1}(x_{i+1}-x_i)(y_i+y_{i+1})} AUC=21i=1m1(xi+1xi)(yi+yi+1)

2.3.4 代价敏感错误率与代价曲线

现实任务中,不同类型的错误所造成的后果不同。

例如,在医院诊断中,错误地把患者诊断为健康人与错误地把健康人诊断为患者,看起来都是犯了一次错误,但后者的影响是增加了进一步检查的麻烦,前者的后果却可能是丧失了拯救生命的最佳时机。

为权衡不同类型错误所造成的不同损失,可为错误赋予”非均等代价“。

以二分任务为例,设定一个“代价矩阵”,其中 c o s t i j cost_{ij} costij表示将第i类样本预测为第j类样本的代价。

真实类别预测第0类预测第1类
第0类0 c o s t 01 cost_{01} cost01
第1类 c o s t 10 {cost_{10}} cost100

一般来说, c o s t i j cost_{ij} costij=0;若将第0类判别为第1类所造成的损失更大,则 c o s t 01 cost_{01} cost01> c o s t 10 cost_{10} cost10;损失程度相差越大, c o s t 01 cost_{01} cost01 c o s t 10 cost_{10} cost10值的差别越大。

在非均等代价下,我们所希望的不再是简单地最小化错误次数,而是希望最小化”总体代价“。

”代价敏感“错误率为
E ( f ; D ; c o s t ) = 1 m ( ∑ x i ∈ D + ∣ ∣ ( f ( x i ) ≠ y i ) × c o s t 01 + ∑ x i ∈ D − ∣ ∣ ( f ( x i ) ≠ y i ) × c o s t 10 ) E(f;D;cost) = {1 \over m} \left( \sum_{x_i\in D^+}||(f(x_i) \neq y_i)\times cost_{01} +\sum_{x_i \in D^-}||(f(x_i)\neq y_i)\times cost_{10} \right) E(f;D;cost)=m1(xiD+(f(xi)=yi)×cost01+xiD(f(xi)=yi)×cost10)
在非均等代价下,ROC曲线不能直接反映出学习器的期望总体代价,而”代价曲线“则可达到该目的。

代价曲线图的横轴是取值为[0,1]的正比例代价
P ( + ) c o s t = p × c o s t 01 p × c o s t 01 + ( 1 − p ) × c o s t 10 P(+)cost= {{p\times cost_{01}} \over {p\times cost_{01}+(1-p)\times cost_{10}}} P(+)cost=p×cost01+(1p)×cost10p×cost01
其中p是样例为正例的概率;纵轴是取值[0,1]的归一化代价
c o s t n o r m = F N R × p × c o s t 01 + F P R × ( 1 − p ) × c o s t 10 p × c o s t 01 + ( 1 − p ) × c o s t 10 cost_{norm} = {{FNR\times p\times cost_{01}+FPR\times (1-p)\times cost_{10}}\over{ p\times cost_{01} + (1-p)\times cost_{10} }} costnorm=p×cost01+(1p)×cost10FNR×p×cost01+FPR×(1p)×cost10
其中FPR是假正例率,FNR= 1-TPR是假反例率。

“规范化”是将不同变化范围的值映射到相同的固定范围中,常见的是[0,1],此时亦称”归一化“。

2.4 比较检验(简单介绍)

有了实验评估方法和性能度量,看起来就能对学习器的性能进行评估比较了:先使用某种实验评估方法测得学习器的某个性能度量结果,然后对这些结果进行比较。但是怎么来做这个“比较”呢?是直接取得性能度量的值为然后”比大小“吗?实际上,机器学习中性能比较这件事要比大家想象的复杂的多。

涉及几个重要因素:

  • 我们希望比较的是泛化性能,然而通过实验评估方法我们获得的是测试集上的性能,两者的对比结果可能未必相同。
  • 测试集上的性能与测试集本身的选择有很大关系,且不论使用不同大小的测试集会得到不同的结果,即使用相同大小的测试集,若包含的测试样例不同,测试结果也会不同。
  • 很多机器学习算法本身有一定的随机性,即便用相同的参数设置在一个测试集上多次运行,其结果也会有不同。

统计假设检验为我们进行学习器性能比较提供了重要依据。

2.4.1 假设检验

假设检验中的“假设”是对学习器泛化错误率分布的某种判断和猜想,例如“ ϵ = ϵ 0 \epsilon=\epsilon_0 ϵ=ϵ0”。现实任务中我们并不知道学习器的泛化错误率,只能获知其测试错误率 ϵ ^ \widehat{\epsilon} ϵ 。泛化错误率与测试错误率未必相同,但直观上,二者接近的可能性比较大,相差很远的可能性小。因此,可根据测试错误率估推出泛化错误率的分布。

泛化错误率为 ϵ \epsilon ϵ的学习器在一个样本上犯错的概率为 ϵ \epsilon ϵ;测试错误率 ϵ ^ \widehat{\epsilon} ϵ 意味着在m个测试样本中恰有 ϵ ^ × m \widehat{\epsilon}\times m ϵ ×m个被误分类。

假定测试样本是从样本总体分布中独立采样而得,那么泛化错误率为 ϵ \epsilon ϵ的学习器将其中 m ‘ m^` m个样本误分类、其余样本全都分类正确的概率是$ {m \choose m`}\epsilon^{m^`}{(1-\epsilon)}{m-m^`} ; 由 此 可 估 算 出 其 恰 将 ;由此可估算出其恰将 \widehat\epsilon \times m 个 样 本 误 分 类 的 概 率 如 下 所 示 , 这 也 表 达 了 在 包 含 m 个 样 本 的 测 试 集 上 , 泛 化 错 误 率 为 个样本误分类的概率如下所示,这也表达了在包含m个样本的测试集上,泛化错误率为 m\epsilon 的 学 习 器 被 测 得 测 试 错 误 率 为 的学习器被测得测试错误率为 \widehat\epsilon$的概率:
P ( ϵ ^ ; ϵ ) = ( m ϵ ^ × m ) ϵ ϵ ^ × m ( 1 − ϵ ) m − ϵ ^ × m P(\widehat\epsilon;\epsilon) = {m \choose {\widehat\epsilon\times m}}\epsilon^{\widehat\epsilon\times m}{(1-\epsilon)}^{m-\widehat\epsilon\times m} P(ϵ ;ϵ)=(ϵ ×mm)ϵϵ ×m(1ϵ)mϵ ×m
这里一开始没有理解,其实这里目的就是验证泛化错误率和测试错误率不大。

给定测试错误率,则解 ∂ P ( ϵ ^ ; ϵ ) / ∂ ϵ = 0 \partial P(\widehat\epsilon;\epsilon)/\partial\epsilon = 0 P(ϵ ;ϵ)/ϵ=0可知, P ( ϵ ^ ; ϵ ) P(\widehat\epsilon;\epsilon) P(ϵ ;ϵ) ϵ = ϵ ^ \epsilon= \widehat\epsilon ϵ=ϵ 时最大, ∣ ϵ − ϵ ^ ∣ \lvert \epsilon - \widehat\epsilon\rvert ϵϵ 增大时, P ( ϵ ^ ; ϵ ) P(\widehat\epsilon;\epsilon) P(ϵ ;ϵ)减小。

2.4.2 交叉验证t检验

对两个学习器A和B,若我们使用k折交叉验证法得到的测试错误率分别为 ϵ 1 A , ϵ 2 A , . . . , ϵ k A \epsilon_1^A,\epsilon_2^A,...,\epsilon_k^A ϵ1A,ϵ2A,...,ϵkA ϵ 1 B , ϵ 2 B , . . . , ϵ k B \epsilon_1^B,\epsilon_2^B,...,\epsilon_k^B ϵ1B,ϵ2B,...,ϵkB,其中 ϵ i A \epsilon_i^A ϵiA ϵ i B \epsilon_i^B ϵiB是在相同的第 i i i 折训练/测试集上得到的结果,则可用k折交叉验证"成对 t 检验"来进行比较检验。

基本思想是若两个学习器的性能相同,则它们使用相同的训练/测试集得到的测试错误率应相同,即 ϵ i A = ϵ i B \epsilon_i^A = \epsilon_i^B ϵiA=ϵiB

2.4.3 McNemar检验

对于二分类问题,使用留出法不仅可估计出学习器A和B的测试错误率,还可获得两学习器分类结果的差别,即两者都正确、都错误、一个正确另一个错误的样本数,如“列联表”,

算法B算法A 正确算法A 错误
正确 e 00 e_{00} e00 e 01 e_{01} e01
错误 e 10 e_{10} e10 e 11 e_{11} e11

若我们做的假设是两学习器性能相同,则应有 e 01 = e 10 e_{01} = e_{10} e01=e10,那么变量 ∣ e 01 − e 10 ∣ \lvert e_{01} - e_{10} \rvert e01e10应当服从正态分布。

2.4.4 Friedman检验与Nemenyi后续检验

交叉验证 t 检验和McNemar检验都是在一个数据集上比较两个算法的性能,而在很多时候,我们会在一组数据集上对多个算法进行比较。

当有多个算法参与比较时,一种做法是在每个数据集上分别列出两两比较的结果,而在两两比较时可用前述方法;令一种方法更为直接,即使用基于算法排序的Friedman检验。

2.5 偏差与方差

对学习算法除了通过实验估计其泛化性能,人们往往还希望了解它”为什么“具有这样的性能。”偏差-方差分解“是解释学习算法泛化性能的一种重要工具。

对测试样本x,令 y D y_D yD为x在数据集中的标记,y为x的真实标记, f ( x ; D ) f(x;D) f(x;D)为训练集D上学得模型f在x上的预测输出。

以回归任务为例,学习算法的期望预测为
f ‾ ( x ) = E D [ f ( x ; D ) ] \overline f(x) = E_D[f(x;D)] f(x)=ED[f(x;D)]
使用样本数相同的不同训练集产生的方差
v a r ( x ) = E D [ ( f ( x ; D ) − f ‾ ( x ) ) 2 ] var(x) = E_D[{(f(x;D)-\overline f(x))}^2] var(x)=ED[(f(x;D)f(x))2]
噪声为
ε 2 = E D [ ( y D − y ) 2 ] \varepsilon^2 = E_D[{(y_D-y)}^2] ε2=ED[(yDy)2]
即真实标记与数据集中的实际标记间的偏差。

期望输出与真实标记的差别称为偏差(bias),即
b a i s 2 ( x ) = ( f ‾ ( x ) − y ) 2 bais^2(x) = {(\overline f(x)-y)}^2 bais2(x)=(f(x)y)2
为便于讨论,假定噪声期望为零,即 E D [ y D − y ] = 0 E_D[y_D-y] = 0 ED[yDy]=0。通过简单的多项式展开,可对算法的期望泛化误差进行分解:

bias-variance-proof

可知,泛化误差可分解为偏差、方差与噪声之和。

偏差度量了学习算法的期望预测与真实结果的偏离程度,即刻画了学习算法本身的拟合能力;

方差度量了样大小的训练集的变动所导致的学习性能的变化,即刻画了数据扰动所造成的影响;

噪声则表达了在当前任务上任何学习算法所能达到的期望泛化误差的下界,即刻画了学习问题本身的难度。

偏差-方差分解说明,泛化性能是由学习算法的能力、数据的充分性以及学习任务本身的难度所共同决定的。给定学习任务,为了取得好的泛化性能,则需使偏差较小,即能充分拟合数据,并且使方差较小,即使得数据扰动产生的影响小。

一般来说,偏差与方差是有冲突的,这称为偏差-方差窘境。

给定学习任务,假定我们能控制学习算法的训练程度,则在训练不足时,学习器的拟合能力不够强,训练数据的扰动不足以使学习器产生显著变化,此时偏差主导了泛化错误率;随着训练程度的加深,学习器的拟合能力逐渐加强,训练数据发生的扰动渐渐能被学习器学到,方差逐渐主导了泛化错误率;在训练程度充足后,学习器的拟合能力已非常强,训练数据发生的轻微扰动都会导致学习器发生显著变化,若训练数据自身、非全局性的特性被学习器学到了,则将发生过拟合。

img

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值