梯度下降法(Gradient descent)


在了解梯度下降法之前我们首先来了解一下梯度的概念

梯度

设函数 z = f ( x , y ) z=f(x,y) z=f(x,y)在平面区域 D D D内具有一阶连续偏导数,则对于每一点 P ( x , y ) ∈ D P(x,y)\in{D} P(x,y)D,都可以定出一个向量 ∂ f ∂ x i ⃗ + ∂ f ∂ y j ⃗ ( 其 中 i , ⃗ , j ⃗ ) 为 单 位 向 量 \frac{\partial f}{\partial x} \vec{i}+\frac{\partial f}{\partial y} \vec{j}(其中\vec{i,},\vec{j})为单位向量 xfi +yfj i, ,j ,这向量称为函数 z = f ( x , y ) z=f(x,y) z=f(x,y)在点 P ( x , y ) P(x,y) P(x,y)的梯度,记为 ∇ z = g r a d → f ( x , y ) = ∂ f ∂ x i ⃗ + ∂ f ∂ y j ⃗ \nabla{z}=\overrightarrow{g r a d} f(x, y)=\frac{\partial f}{\partial x} \vec{i}+\frac{\partial f}{\partial y} \vec{j} z=grad f(x,y)=xfi +yfj
函数在某点的梯度是这样一个向量:它的方向与取得最大方向导数的方向一致,它的模为方向导数的最大值。
举例:
f ( x , y , z ) = 4 x + 3 y + 10 z ∂ f = ⟨ d f d x , d f d y , d f d z ⟩ = ⟨ 4 , 3 , 10 ⟩ \begin{aligned} f(x, y, z) &=4 x+3 y+10 z \\ \partial f &=\left\langle\frac{d f}{d x}, \frac{d f}{d y}, \frac{d f}{d z}\right\rangle \\ &=\langle 4,3,10\rangle \end{aligned} f(x,y,z)f=4x+3y+10z=dxdf,dydf,dzdf=4,3,10
需要了解几个梯度的性质

  • 梯度是一个向量
  • 梯度的方向是使函数在这一点值上升最快方向(可以由方向导数推得)
  • 在单变量的函数中,梯度其实就是函数的微分,(例: d ( x 2 ) d x = 2 x \frac{d\left(x^{2}\right)}{d x}=2 x dxd(x2)=2x)代表着函数在某个给定点的切线的斜率
  • 在多变量函数中,梯度是一个向量,向梯度的方向就指出了函数在给定点的上升最快的方向

梯度下降

运用梯度下降法是为了求解无约束最优化问题,即假设 f ( x ) f(x) f(x)是在 R n R^{n} Rn上具有一阶连续偏导数的函数,要求解的无约束最优化问题是 min ⁡ x ∈ R n f ( x ) \min _{x \in \mathbf{R}^{n}} f(x) xRnminf(x)
举例说明:
如图,有一个函数 L L L,我们任选一个点开始,要求得使函数值 L L L最小的点;类似于一个人站在一座山中,要到达山谷中最低的位置(寻找最小点),那么我们应该从出发点开始(任选一点 x 0 x^0 x0),每次都选择往最陡峭的方向走(寻找 x k + 1 x^{k+1} xk+1),每走一段距离(这里就相当于 η \eta η)判断一次下一个最陡峭的地方(相当于当前点的梯度方向),这样就能到达一个相对最低的点(极小点),并求得该点的位置(参数值)。
在这里插入图片描述
梯度下降法是一种迭代算法.选取适当的初值 x 0 x^{0} x0,不断迭代,更新 x x x的值,进行目标函数的极小化,直到收敛.由于负梯度方向是使函数值下降最快的方向,在迭代的每一步,以负梯度方向更新 x x x的值,从而达到减少函数值的目的。

我们通常运用梯度下降法来求解使得损失函数 f f f的值最小的点,
运用梯度下降解决一元函数的问题,
例如:假设 L L L图像如图所示,求 w ∗ = arg ⁡ min ⁡ w L ( w ) w^{*}=\arg \min _{w} L(w) w=argwminL(w)
在这里插入图片描述

  1. 从图像上选取适当一点 w 0 w^0 w0作为起始点;
  2. 求取 L 在 w 0 L在w^0 Lw0点的微分 d L d w ∣ w = w 0 \left.\frac{d L}{d w}\right|_{w=w^{0}} dwdLw=w0
  3. 然后变化根据 L 在 w 0 L在w^0 Lw0点的微分值选择下一步要走的方向:我们要找最小的点,则当微分值大于0时,即图中2那一点的情况,此时应该往左走,即 w 1 ( 下 一 步 要 到 达 的 位 置 ) 等 于 w 0 w^1(下一步要到达的位置)等于w^0 w1()w0减去一个值,当微分值小于0,即图中1那一点的情况,此时应该往右走,则 w 1 w^1 w1等于 w 0 w^0 w0加上一个值,综上 w 1 可 写 成 w^1可写成 w1
    w 1 ← w 0 − η d L d w ∣ w = w 0 其 中 η 表 示 步 长 ( 即 下 一 次 移 动 的 距 离 ( 又 称 学 习 率 ) , − d L d w ∣ w = w 0 微 分 决 定 前 进 方 向 ) w^{1} \leftarrow w^{0}-\eta\left.\frac{d L}{d w}\right|_{w=w^{0}}\\其中\eta表示步长(即下一次移动的距离(又称学习率),-\left.\frac{d L}{d w}\right|_{w=w^{0}}微分决定前进方向) w1w0ηdwdLw=w0ηdwdLw=w0
  4. 然后不断重复2,3步骤,即 w k + 1 ← w k − η d L d w ∣ w = w k w^{k+1} \leftarrow w^{k}-\eta\left.\frac{d L}{d w}\right|_{w=w^{k}} wk+1wkηdwdLw=wk,直到终止条件(微分值为零或找不到更小的值等),就有可能找到最小值点或者极小值点。需要注意的是我们还需要调整 η \eta η的大小,当离极小值点越来越近( L L L的变化越来越小时),需要减小 η \eta η的值,即减小步长。

类似的,我们可以得到二元及多元的情况:
假设函数 L ( w , b ) L(w,b) L(w,b),求 w ∗ , b ∗ = arg ⁡ min ⁡ w , b L ( w , b ) w^{*}, b^{*}=\arg \min _{w, b} L(w, b) w,b=argw,bminL(w,b)

  1. 随机选取初始点 w 0 , b 0 w^0,b^0 w0,b0;
  2. 因为 L ( w , b ) L(w,b) L(w,b)包含两个自变量 w , b w,b w,b,因此需要求偏微分: ∂ L ∂ w ∣ w = w 0 , b = b 0 , ∂ L ∂ b ∣ w = w 0 , b = b 0 ; \left.\frac{\partial L}{\partial w}\right|_{w=w^{0}, b=b^{0}},\left.\frac{\partial L}{\partial b}\right|_{w=w^{0}, b=b^{0}}; wLw=w0,b=b0,bLw=w0,b=b0;
    3.类似一元的情况: w 1 ← w 0 − η ∂ L ∂ w ∣ w = w 0 , b = b 0 b 1 ← b 0 − η ∂ L ∂ b ∣ w = w 0 , b = b 0 w^{1} \leftarrow w^{0}-\eta\left.\frac{\partial L}{\partial w}\right|_{w=w^{0}, b=b^{0}} \quad b^{1} \leftarrow b^{0}-\eta\left.\frac{\partial L}{\partial b}\right|_{w=w^{0}, b=b^{0}} w1w0ηwLw=w0,b=b0b1b0ηbLw=w0,b=b0
    即:根据第k步决定k+1步, w k + 1 ← w k − η ∂ L ∂ w ∣ w = w k , b = b k b k + 1 ← b k − η ∂ L ∂ b ∣ w = w k , b = b k w^{k+1} \leftarrow w^{k}-\eta\left.\frac{\partial L}{\partial w}\right|_{w=w^{k}, b=b^{k}} \quad b^{k+1} \leftarrow b^{k}-\eta\left.\frac{\partial L}{\partial b}\right|_{w=w^{k}, b=b^{k}} wk+1wkηwLw=wk,b=bkbk+1bkηbLw=wk,b=bk
    因为多元的情况可以将梯度写成如下形式:
    ∇ L = [ ∂ L ∂ w ∂ L ∂ b ] g r a d i e n t \nabla L=\left[\begin{array}{l}{\frac{\partial L}{\partial w}} \\ {\frac{\partial L}{\partial b}}\end{array}\right]_{gradient} L=[wLbL]gradient
    所以有:
    [ w k + 1 b k + 1 ] = [ w k b k ] − η ∇ L ( w , b ) = [ w k b k ] − η [ ∂ L ∂ w ∣ w = w k , b = b k ∂ L ∂ b ∣ w = w k , b = b k ] , 类 似 地 , − η ∇ L ( w , b ) 决 定 方 向 \left[\begin{array}{c}{w^{k+1}} \\ {b^{k+1}}\end{array}\right]=\left[\begin{array}{l}{w^{k}} \\ {b^{k}}\end{array}\right]-\eta\nabla L(w,b)=\left[\begin{array}{l}{w^{k}} \\ {b^{k}}\end{array}\right]-\eta\left[\begin{array}{l}{\frac{\partial L}{\partial w}|_{w=w^k,b=b^k}} \\ {\frac{\partial L}{\partial b}|_{w=w^k,b=b^k}}\end{array}\right],类似地,-\eta\nabla L(w,b)决定方向 [wk+1bk+1]=[wkbk]ηLw,b=[wkbk]η[wLw=wk,b=bkbLw=wk,b=bk],ηLw,b
    步长(学习率)的大小可能对结果产生不同的影响。如图所示
    不同大小的 η \eta η可能会造成下面图中的情况
    在这里插入图片描述
    有关调整步长的方法可以参考文章步长(学习率learning rate)

全局最小和局部最小

如图所示,是在运用梯度下降的过程中,从某一点出发可能会陷入局部最小(即在某一邻域内,它是最小值)找不到最优解。
在这里插入图片描述
在这里插入图片描述
可以使用一些方法来跳出局部最小:

  • 以多组不同参数值初始化多个神经网络,按标准方法训练后,取其中误差最小的解作为最终参数.这相当于从多个不同的初始点开始搜索,这样就可能陷入不同的局部极小,从中进行选择有可能获得更接近全局最小的结果.
  • 使用“模拟退火”(simulated annealing) 技术模拟退火在每-步都以一定的概率接受比当前解更差的结果,从而有助于“跳出”局部极小.在每步迭代过程中,接受“次优解”的概率要随着时间的推移而逐渐降低,从而保证算法稳定,
  • 使用随机梯度下降.与标准梯度下降法精确计算梯度不同,随机梯度下降法在计算梯度时加入了随机因素.于是,即便陷入局部极小点,它计算出的梯度仍可能不为零,这样就有机会跳出局部极小继续搜索.

梯度下降法可用于求函数的最小值,即通过样本值运用梯度下降法求使得误差最小的参数值,
前面我们介绍的线性模型利用最小二乘法估测误差,求 w ∗ , b ∗ w^*,b^* w,b,即求: ( w ∗ , b ∗ ) = a r g min ⁡ ( w , b ) ( E ( w , b ) ) = a r g min ⁡ ( w , b ) ∑ i = 1 n ( w x i + b − y i ) 2 (w^*,b^*)=arg \min_{(w,b)}(E(w,b))=arg \min_{(w,b)}\sum_{i=1}^{n}(wx_i+b-y_i)^2 w,b=arg(w,b)min(E(w,b))=arg(w,b)mini=1n(wxi+byi)2就可以用梯度下降法来解:这里误差函数 E ( w , b ) = ∑ i = 1 n ( w x i + b − y i ) 2 E(w,b)=\sum_{i=1}^{n}(wx_i+b-y_i)^2 E(w,b)=i=1n(wxi+byi)2j就是损失函数 L L L,分别对 w , b w,b w,b求偏导:
∂ L ∂ w = ∑ i = 1 n 2 ( b + w ⋅ x i − y i ) ( x i ) \frac{\partial L}{\partial w}= \sum_{i=1}^{n} 2\left(b+w \cdot x_{i}-y_i\right)\left(x_{i}\right) wL=i=1n2(b+wxiyi)(xi)
∂ L ∂ b = ∑ i = 1 n 2 ( b + w ⋅ x i − y i ) ∗ ( 1 ) \frac{\partial L}{\partial b}=\sum_{i=1}^{n} 2(b+w\cdot{x_i}-y_i)*(1) bL=i=1n2(b+wxiyi)(1)
这里 ( x i , (x_i, xi,是已知值(样本点), w , b w,b w,b是未知,运用梯度下降法求最佳的 w , b w,b w,b使得损失函数 L ( w , b ) L(w,b) L(w,b)最小。

Momentum(动量)

在物理学上我们知道,一个球从山坡上滚下来,到达最低点时不一定会停下来,带有动量会继续往前走。如图,这个球可能会冲出局部最低点。那么我们应该怎样将这点知识运用到梯度下降法中,帮助我们调参时逃出局部最小点,从而找到最优解呢?
在这里插入图片描述
如图,当球在最右边时,如果动能再向右,而通过梯度计算告诉我们应该往左走,等动能足够大时就可以跳出局部最小。
在这里插入图片描述
梯度下降加入Momentum:

  • 每次移动时计算梯度的方向,
  • 这里的动量以前一次所移动的方向当做动量。
  1. 起始点 w 0 w^{0} w0,Momentum: v 0 = 0 v^0=0 v0=0;
  2. 计算 w 0 w^0 w0处的梯度 g 0 = ∇ L ( w 0 ) g^0=\nabla L\left(w^{0}\right) g0=L(w0),动量 v 1 = λ v 0 − η ∇ L ( w 0 ) \mathrm{v}^{1}=\lambda \mathrm{v}^{0}-\eta \nabla L\left(w^{0}\right) v1=λv0ηL(w0)
  3. w 1 = w 0 + v 1 w^1=w^0+v^1 w1=w0+v1,计算 g 1 g^1 g1, v 2 = λ v 1 − η ∇ L ( w 1 ) v^{2}=\lambda v^{1}-\eta \nabla L\left(w^{1}\right) v2=λv1ηL(w1);
    4.重复上述过程, w k + 1 = w k + v k + 1 w^{k+1}=w^k+v{k+1} wk+1=wk+vk+1, v k + 2 = λ v k + 1 − η ∇ L ( w k + 1 ) v^{k+2}=\lambda v^{k+1}-\eta \nabla L\left(w^{k+1}\right) vk+2=λvk+1ηL(wk+1)

在这里插入图片描述
v k v^k vk其实是之前所有梯度的和,即 ∑ i = 1 k − 1 ∇ L ( w i ) \sum_{i=1}^{k-1}\nabla L\left(w^{i}\right) i=1k1L(wi);
因为:
v 0 = 0 v 1 = − η ∇ L ( w 0 ) v 2 = − λ η ∇ L ( w 0 ) − η ∇ L ( w 1 ) . . . \begin{aligned} \mathrm{v}^{0} &=0 \\ \mathrm{v}^{1} &=-\mathrm{\eta} \nabla L\left(w^{0}\right) \\ \mathrm{v}^{2} &=-\lambda \eta \nabla L\left(w^{0}\right)-\eta \nabla L\left(w^{1}\right) \end{aligned}\\... v0v1v2=0=ηL(w0)=ληL(w0)ηL(w1)...
(上述做法可以在Adam中找到原型。)

比较几种梯度下降的优缺点

1,批量梯度下降法(Batch Gradient Descent) :在更新参数时每次都使用所有的样本组成一个矩阵来对参数进行更新。

优点:全局最优解,能保证每一次更新权值,都能降低损失函数;易于并行实现。

缺点:当样本数目很多时,训练过程会很慢。

2,随机梯度下降法(Stochastic Gradient Descent):在更新参数时每次只使用一个样本来进行更新。每一次跟新参数都用一个样本,更新很多次。如果样本量很大的情况(例如几十万),那么可能只用其中几万条或者几千条的样本,就已经将参数迭代到最优解了,对比上面的批量梯度下降,迭代一次需要用到十几万训练样本,一次迭代不可能最优,如果迭代10次的话就需要遍历训练样本10次,这种方式计算复杂度太高。

优点:训练速度快;

缺点:准确度下降,并不是全局最优;不易于并行实现。从迭代的次数上来看,随机梯度下降法迭代的次数较多,在解空间的搜索过程看起来很盲目。噪音很多,使得它并不是每次迭代都向着整体最优化方向。

3,小批量梯度下降法(Mini-batch Gradient Descen):在更新每一参数时都使用一部分样本来进行更新。例如,有样本30w,batch_size=100,需迭代3000次,而随机梯度下降需迭代30w次,克服上面两种方法的缺点,又同时兼顾两种方法的优点。运用了GPU并行计算的优点。
  在这里插入图片描述

4,三种方法使用的情况:如果样本量比较小,采用批量梯度下降算法。如果样本太大,或者在线算法,使用随机梯度下降算法。在实际的一般情况下,采用小批量梯度下降算法。

数学知识(仅做了解)

学过泰勒公式的同学可以知道,假设一个函数 h ( x ) 在 x = x 0 的 邻 域 内 n 阶 可 导 h(x)在x=x_0的邻域内n阶可导 h(x)x=x0n,则:
h ( x ) = ∑ k = 0 ∞ h ( k ) ( x 0 ) k ! ( x − x 0 ) k = h ( x 0 ) + h ′ ( x 0 ) ( x − x 0 ) + h ′ ′ ( x 0 ) 2 ! ( x − x 0 ) 2 + … \begin{aligned} h(x) &=\sum_{k=0}^{\infty} \frac{h^{(k)}\left(x_{0}\right)}{k !}\left(x-x_{0}\right)^{k} \\ &=h\left(x_{0}\right)+h^{\prime}\left(x_{0}\right)\left(x-x_{0}\right)+\frac{h^{\prime \prime}\left(x_{0}\right)}{2 !}\left(x-x_{0}\right)^{2}+\ldots \end{aligned} h(x)=k=0k!h(k)(x0)(xx0)k=h(x0)+h(x0)(xx0)+2!h(x0)(xx0)2+
当 x − > x 0 时 , h ( x ) ≈ h ( x 0 ) + h ′ ( x 0 ) ( x − x 0 ) 当x->x_0时, h(x) \approx h\left(x_{0}\right)+h^{\prime}\left(x_{0}\right)\left(x-x_{0}\right) x>x0h(x)h(x0)+h(x0)(xx0)
多元函数的情况:
h ( x , y ) = h ( x 0 , y 0 ) + ∂ h ( x 0 , y 0 ) ∂ x ( x − x 0 ) + ∂ h ( x 0 , y 0 ) ∂ y ( y − y 0 ) + . . . h(x, y)=h\left(x_{0}, y_{0}\right)+\frac{\partial h\left(x_{0}, y_{0}\right)}{\partial x}\left(x-x_{0}\right)+\frac{\partial h\left(x_{0}, y_{0}\right)}{\partial y}\left(y-y_{0}\right)+... h(x,y)=h(x0,y0)+xh(x0,y0)(xx0)+yh(x0,y0)(yy0)+...
当 ( x , y ) − > ( x 0 , y 0 ) 时 , h ( x , y ) ≈ h ( x 0 , y 0 ) + ∂ h ( x 0 , y 0 ) ∂ x ( x − x 0 ) + ∂ h ( x 0 , y 0 ) ∂ y ( y − y 0 ) 当(x,y)->(x_0,y_0)时, h(x, y) \approx h\left(x_{0}, y_{0}\right)+\frac{\partial h\left(x_{0}, y_{0}\right)}{\partial x}\left(x-x_{0}\right)+\frac{\partial h\left(x_{0}, y_{0}\right)}{\partial y}\left(y-y_{0}\right) (x,y)>(x0,y0)h(x,y)h(x0,y0)+xh(x0,y0)(xx0)+yh(x0,y0)(yy0)
有二元函数 L ( x , y ) , n L(x,y),n L(x,y),n阶可偏导,在( a , b a,b a,b)这一点展开,则
L ( x , y ) ≈ L ( a , b ) + ∂ L ( a , b ) ∂ x ( x − a ) + ∂ L ( a , b ) ∂ y ( y − b ) \mathrm{L}(x,y) \approx \mathrm{L}(a, b)+\frac{{\partial \mathrm{L}(a, b)}}{\partial x}\left(x-a\right)+\frac{\partial \mathrm{L}(a, b)}{\partial{y}}\left(y-b\right) L(xy)L(a,b)+xL(a,b)(xa)+yL(a,b)(yb)
令: s = L ( a , b ) , u = ∂ L ( a , b ) ∂ x , v = ∂ L ( a , b ) ∂ y ; s=L(a,b), u=\frac{{\partial \mathrm{L}(a, b)}}{\partial x},v=\frac{{\partial \mathrm{L}(a, b)}}{\partial y}; s=L(a,b),u=xL(a,b),v=yL(a,b);
L ( a , b ) ≈ s + u ( x − a ) + v ( y − b ) L(a,b)\approx{s+u{(x-a)}}+v(y-b) L(a,b)s+uxa+v(yb)
邻域 ( x − a ) 2 + ( y − b ) 2 ≤ d 2 \left(x-a\right)^{2}+\left(y-b\right)^{2} \leq d^{2} (xa)2+(yb)2d2内找到使 L ( x , y ) L(x,y) L(x,y)最小的点,即与梯度相反的方向,
在这里插入图片描述
即: [ x − a y − b ] = [ Δ x Δ y ] = − η [ u v ] \left[\begin{array}{l}{x-a} \\ {y-b}\end{array}\right]=\left[\begin{array}{c}{\Delta {x}} \\ {\Delta {y}}\end{array}\right]=-\eta\left[\begin{array}{l}{u} \\ {v}\end{array}\right] [xayb]=[ΔxΔy]=η[uv]
可得:
[ x y ] = [ a b ] − η [ u v ] = [ a b ] − η [ ∂ L ( a , b ) ∂ x ∂ L ( a , b ) ∂ y ] , η 要 足 够 小 \left[\begin{array}{l}{x} \\ {y}\end{array}\right]=\left[\begin{array}{l}{a} \\ {b}\end{array}\right]-\eta\left[\begin{array}{l}{u} \\ {v}\end{array}\right]=\left[\begin{array}{l}{a} \\ {b}\end{array}\right]-\eta\left[\begin{array}{c}{\frac{\partial \mathrm{L}(a, b)}{\partial x}} \\ {\frac{\partial \mathrm{L}(a, b)}{\partial y}}\end{array}\right] ,\eta要足够小 [xy]=[ab]η[uv]=[ab]η[xL(a,b)yL(a,b)]η
由此推出梯度下降原理。

def gradient(X,Y,options):
    m,n=X.shape
    theta=np.zeros((n,1))#参数
    cost=J(theta,X,Y)#代价函数
    costs=[cost]
    thetas=[theta]#保存参数
    
    alpha=options.get('alpha',0.01)#学习率
    epsilon=options.get('epsilon',0.00001)#迭代阙值
    maxloop=options.get('maxloop',1000)#最大迭代次数
    theLambda=float(options.get('theLambda',0))#正则化系数
    method=options.get('method','bgd')
    #随机梯度下降
    def _sgd(theta):
        count=0
        converged=False
        while count<maxloop:
            if converged:
                break
                
            for i in range(m):
                h=sigmoid(np.dot(X[i].reshape((1,n)),theta))#逻辑回归
                #np.r_是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等
                theta=theta-alpha*((1.0/m)*X[i].reshape((n,1))*(h-Y[i])+(theLambda/m)*np.r_[[[0]],theta[1:]])
                thetas.append(theta)
                cost=J(theta,X,Y,theLambda)
                costs.append(cost)
                if abs(costs[-1]-costs[-2])<epsilon:
                    converged=True
                    break
            count+=1
        return thetas,costs,count
    #批量梯度下降
    def _bgd(theta):
        count=0
        converged=False
        while count<maxloop:
            if converged:
                break
            h=sigmoid(np.dot(X,theta))
            theta=theta-alpha*((1.0/m)*np.dot(X.T,(h-Y))+(theLambda/m)*np.r_[[[0]],theta[1:]])
            thetas.append(theta)
            cost=J(theta,X,Y,theLambda)
            costs.append(cost)
            if abs(costs[-1]-costs[-2])<epsilon:
                converged=True
                break
            count+=1
        return thetas,costs,count
    methods={'sgd':_sgd,'bgd':_bgd}
    return methods[method](theta)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值