深度学习(4): 深度学习基础1——激活函数和损失函数

在这里插入图片描述

注:转载请标明原文出处链接:https://xiongyiming.blog.csdn.net/article/details/99672818


1 激活函数

首先来看激活函数在神经网络模型中的位置,如下图图 3-1 (a)所示的激活函数实际上隐藏在神经网络模型中的连接线上。前文反复强调神经网络模型中的连接是最重要的,该连接线包括输入矩阵与权值矩阵相乘的过程,还包括激活函数对数据进行激活的过程。

在这里插入图片描述
激活函数的一般性质
(1) 单调可微
一般情况下,我们使用梯度下降算法更新神经网络中的参数,因此必须要求激活函数可微。如果函数是单调递增的,求导后函数必大于零(方便计算),因此需要激活函数具有单调性。

(2) 限制输出值的范围
输入的数据通过神经元上的激活函数来控制输出数值的大小,该输出数值是一个非线性值。通过激活函数求得的数值,根据极限值来判断是否需要激活该神经元 。也就是说,我们可以通过激活函数确定是否对一个神经元的输出感兴趣。

(3) 非线性
因为线性模型的表达能力不够(从数据输入到与权值求和加偏置,都是在线性函数内执行权重与输入数据进行加权求和的过程,如( z = w x + b z = wx + b z=wx+b ),因此激活函数的出现还为神经网络模型加入非线性因素。激活函数拥有上述特性,它存在的核心意义在于:一个没有激活函数的神经网络只不过是一个线性回归模型,它不能表达复杂的数据分布。神经网络中加入了激活函数,相当于引入了非线性因素,从而解决了线性模型不能解决的问题。

选择不同的激活函数对神经网络的训练和预测都有着不同程度的影响,下面来分析神经网络中常用的激活函数及其优缺点。



1.1 线性函数

线性(Linear)函数是最基本的激活函数,其因变量与自变量之间有直接的比例关系。 因此,线性变换类似于线性回归。实际上,在神经网络中线性变换,意味着节点按原样通过数据信号通常与单层神经网络一起使用。


代码示例

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import math

# 线性函数定义
def Linear1(x, W=1.5):
    return W*x

x = np.arange(-2, 2, 0.01)
y_linear1 = Linear1(x)

plt.plot(x, y_linear1, color = '#1797ff',linewidth=3)
ax=plt.gca()
ax.set_xlim([-2, 2])
ax.set_ylim([-2, 2])
ax.axhline(0, linestyle='--', color='gray', linewidth=1)
ax.axvline(0, linestyle='--', color='gray', linewidth=1)
plt.title("Linear")
plt.show()

结果如下
在这里插入图片描述


1.2 Sigmoid函数

Sigmoid 函数是一种在不删除数据的情况下,减少数据的极值或异常值的函数。因此,它能够很好地表达激活的意思,未激活值为0,完全饱和的激活值则为l。在数学上,Sigmoid 激活函数为每个类输出提供独立的概率。表达式如下:
(1) s ( x ) = 1 1 + e − a x s(x) = {1 \over {1 + {e^{ - ax}}}} \tag{1} s(x)=1+eax1(1)

(1) Sigmoid函数优点

  1. Sigmoid 函数的输出映射在[0,1]范围内,函数单调连续,且输出范围有限制,优化稳定;
  2. 易于求导;
  3. 输出值为独立概率,可以用在输出层.

(2) Sigmoid函数缺点

  1. Sigmoid 函数容易饱和,导致训练结果不佳:
  2. 其输出并不是零均值,数据存在偏差,分布不平均.

代码示例

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import math


# sigmoid 函数定义
def sigmoid(x):
    return 1 / (1 + np.exp(-1*x))


x = np.arange(-2, 2, 0.01)
y_sigmoid = sigmoid(x)

plt.plot(x, y_sigmoid, color = '#1797ff', linewidth=3)
ax=plt.gca()
ax.set_xlim([-2, 2])
ax.set_ylim([-1, 1])
ax.axhline(0, linestyle='--', color='gray', linewidth=1)
ax.axvline(0, linestyle='--', color='gray', linewidth=1)
plt.title("sigmoid")
plt.show()

结果如下
在这里插入图片描述


1.3 双曲正切函数

双曲正切函数(hyperbolic tangent function, Tanh)与 Sigmoid 函数类似。不同的是, Tanh函数的归一化范围为[-1,1],而不是[0,1],因此 Tanh 的优点是可以更容易地处理负数。同时,因为 Tanh 是 0 均值,也就解决了Sigmoid 函数的非 0 均值的缺点,所以实际中 Tanh 函数会比 Sigmoid 函数更常用。Tanh函数表达式如下:
(2) t a n h ( x ) = sinh ⁡ ( x ) cosh ⁡ ( x ) = e x − e − x e x + e − x {\rm{tanh}}(x) = {{\sinh (x)} \over {\cosh (x)}} = {{{e^x} - {e^{ - x}}} \over {{e^x} + {e^{ - x}}}} \tag{2} tanh(x)=cosh(x)sinh(x)=ex+exexex(2)

(1) 双曲正切函数优点

  1. Tanh 函数比 Sigmoid 函数收敛速度更快,更加易于训练;
  2. 其输出以 0 为中心,数据分布平均.

(2) 双曲正切函数缺点
没有改变Sigmoid函数由饱和性引起的梯度消失问题.


代码示例

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import math

# tanh函数定义
def tanh(x):
    return np.tanh(x)


x = np.arange(-2, 2, 0.01)
y_tanh = tanh(x)

plt.plot(x, y_tanh , color = '#1797ff', linewidth=3)
ax=plt.gca()
ax.set_xlim([-2, 2])
ax.set_ylim([-1, 1])
ax.axhline(0, linestyle='--', color='gray', linewidth=1)
ax.axvline(0, linestyle='--', color='gray', linewidth=1)
plt.title("tanh")
plt.show()

结果如下
在这里插入图片描述


1.4 ReLU函数

ReLU (Rectified Linear Unit, ReLU) 函数(修正线性单元)满足仿生学中的稀疏性,只有当输入值高于一定数目时才激活该神经元节点。当输入值低于0时进行限制,当输入上升到某一闽值以上时,函数中的自变量与因变量呈线性关系。ReLU函数表达式如下所示:
(3) r e l u ( x ) = max ⁡ ( 0 , x ) {\rm{relu}}(x) = \max (0,x) \tag{3} relu(x)=max(0,x)(3)
ReLU 函数对比 Sigmoid 函数主要有如下 3 点变化:

  1. 单侧抑制;
  2. 相对宽阔的兴奋边界;
  3. 稀疏激活性.

(1) ReLU 函数优点

  1. 相比 Sigmoid 函数和 Tanh 函数, ReLU 函数在随机梯度下降算法中能够快速收敛;
  2. ReLU 函数的梯度为 0 或常数,因此可以缓解梯度消散问题;
  3. ReLU 函数引入稀疏激活性,在无监督预训练时也能有较好的表现.

(2) ReLU 函数缺点

  1. ReLU 神经元在训练中不可逆地死亡;
  2. 随着训练的进行,可能会出现神经元死亡、权重无法更新的现象,流经神经元的梯度从该点开始将永远是零。

注:正是因为 ReLU 函数比其他激活函数更适合在神经网络中作为激活函数,或者说优点更加明显,因此综合速率和效率,神经网络中大部分激活函数都使用了ReLU函数 。


代码示例

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import math

# relu函数定义
def relu(data):
    return np.maximum(0, data)

x = np.arange(-2, 2, 0.01)
y_relu = relu(x)

plt.plot(x, y_relu , color = '#1797ff', linewidth=3)
ax=plt.gca()
ax.set_xlim([-2, 2])
ax.set_ylim([-1, 1])
ax.axhline(0, linestyle='--', color='gray', linewidth=1)
ax.axvline(0, linestyle='--', color='gray', linewidth=1)
plt.title("relu")
plt.show()

结果如下

在这里插入图片描述

还有一种是带泄露修正线性单元(Leaky ReLU)的输出对负值输入有很小的坡度。由于导数总是不为零,这能减少静默神经元的出现,允许基于梯度的学习(虽然会很慢)。Leaky ReLU函数表达如下:
(4)  Leak relu  ( x ) = { ε x  if  x &lt; 0 x  else  \text { Leak relu }(x)=\left\{\begin{array}{ll}{\varepsilon x} &amp; {\text { if } x&lt;0} \\ {x} &amp; {\text { else }}\end{array}\right. \tag{4}  Leak relu (x)={εxx if x<0 else (4)


代码示例

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import math

# Leak relu函数定义
def leak_relu(data, epsilon=0.1):
    return np.maximum(epsilon * data, data)

x = np.arange(-2, 2, 0.01)
y_leak_relu = leak_relu(x)

plt.plot(x, y_leak_relu , color = '#1797ff', linewidth=3)
ax=plt.gca()
ax.set_xlim([-2, 2])
ax.set_ylim([-1, 1])
ax.axhline(0, linestyle='--', color='gray', linewidth=1)
ax.axvline(0, linestyle='--', color='gray', linewidth=1)
plt.title("Leak_relu")
plt.show()


结果如下
在这里插入图片描述


1.5 Softmax函数

Softmax 函数的本质是将一个K维的任意实数向量,压缩(映射)成另一个 K 维的实数向量,其中向量中的每个元素取值都介于(0, 1)范围内 。
Softmax 是对逻辑回归(Logistic Regression, LR)的推广,逻辑回归用于处理二分类问题,其推广Softmax 回归则用于处理多分类问题。
Softmax 激活函数表达式如下:
(5) s o f t m a x ( x j ) = e x j ∑ k = 1 K e x k {\rm{softmax}}({x_j}) = {{{e^{{x_j}}}} \over {\sum\nolimits_{k = 1}^K {{e^{{x_k}}}} }} \tag{5} softmax(xj)=k=1Kexkexj(5)

注:Softmax 函数通常作为输出层的激活函数。

那么问题来了,为什么通常使用Softmax 函数作为输出层的激活函数? 举个例子来说明。
现假设有一个多分类问题,但是我们只关心这些类别的最高得分概率,那么将会使用一个带有最大似然估计函数的Softmax 输出层来获得所有类别输出概率的最大值。例如神经网络的分类有 3 个,分别为“野马”,河马”,“斑马”,使用 Softmax 作为输出层的激活函数最后只能得到一个最大的分类概率如:野马(0.6)、河马(0.1)、斑马(0.3), 其中最大值为野马(0.6)。


代码示例

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import math


# softmax函数定义
def softmax(x):
    return np.exp(x) / np.sum(np.exp(x))

x = np.arange(-2, 2, 0.01)
y_softmax = softmax(x)

plt.plot(x, y_softmax, color = '#1797ff', linewidth=3)
ax=plt.gca()
ax.set_xlim([-2, 2])
ax.set_ylim([-0.001, 0.01])
ax.axhline(0, linestyle='--', color='gray', linewidth=1)
ax.axvline(0, linestyle='--', color='gray', linewidth=1)
plt.title("Softmax")
plt.show()

结果如下

在这里插入图片描述

Softmax函数 与 Sigmoid 函数比较

SigmoidSoftmax
公式 s ( x ) = 1 1 + e − a x s(x) = {1 \over {1 + {e^{ - ax}}}} s(x)=1+eax1 s o f t m a x ( x j ) = e x j ∑ k = 1 K e x k {\rm{softmax}}({x_j}) = {{{e^{{x_j}}}} \over {\sum\nolimits_{k = 1}^K {{e^{{x_k}}}} }} softmax(xj)=k=1Kexkexj
含义非线性映射离散概率分布
任务二分类多分类
定义域单个数值一维向量
值域(0,1)[0,1]
结果值和正数恒为1

1.6 激活函数的选择

在选择激活函数时, 一般隐层选择 Leak ReLU 函数会得到较为理想的效果 。当然这不是恒定的规律,我们可以尝试使用 Sigmoid 函数作为隐层激活函数,但注意使用时尽量不要超过太多隐层。另外可以使用 Tanh 函数来代替 Sigmoid 函数观察模型的精确率曲线图。如果直接使用 ReLU 函数作为激活函数,注意梯度下降算法的学习率参数不能设置得过高,避免神经元的大量“消亡”。对于输出层,一般使用 softmax函数获得同分布最高概率作为输出结果。 此外,可以加入 Batch Normalization (BN)层,让下一层的输入数据具有相同的分布。如果遇到神经网络训练时收敛速度慢,或梯度爆炸或者梯度消失等无法训练的状况都可以尝试加入 BN层,然后观察其训练结果。



2 损失函数

2.1 损失函数定义

在机器学习任务中,大部分监督学习算法都会有一个目标函数 (Objective Function),算法对该目标函数进行优化,称为优化算法的过程。 例如在分类或者回归任务中,使用损失函数( Loss Function )作为其目标函数对算法模型进行优化 。
在BP神经网络中,一般推导中,使用均方误差作为损失函数,而在实际中,常用交叉熵作为损失函数。如下图所示,我们可以清晰地观察到不同的损失函数在梯度下降过程中的收敛速度和性能都是不同的。

  1. 均方误差作为损失函数收敛速度慢,可能会陷入局部最优解;
  2. 而交叉熵作为损失函数的收敛速度比均方误差快,且较为容易找到函数最优解.

因此了解损失函数的类型并掌握损失函数的使用技巧,有助于加深对深度学习的认知.

在这里插入图片描述


损失函数是用来评价网络模型的输出的预测值 Y ^ = f ( X ) {\bf{\hat Y}} = f({\bf{X}}) Y^=f(X) 与真实值 Y {\bf{Y}} Y之间的差异。我们使用 L ( Y , Y ^ ) L({\bf{Y}},{\bf{\hat Y}}) L(Y,Y^)来表示损失函数(非负实值函数)。损失函数越小说明网络模型的性能越好。我们的目的让损失函数尽可能的小。
假设网络模型中有 N N N 个样本,样本的输入和输出向量为 ( X , Y ) = ( x i , y i ) , i ∈ [ 1 , N ] \left( {{\bf{X}},{\bf{Y}}} \right) = \left( {{x_i},{y_i}} \right),i \in \left[ {1,N} \right] (X,Y)=(xi,yi),i[1,N],则损失函数 为每一个输出预测值与真实值的误差之和。
(6) L ( Y , Y ^ ) = ∑ i = 0 N l ( y i , y ^ i ) L({\bf{Y}},{\bf{\hat Y}}) = \sum\limits_{i = 0}^N {l\left( {{y_i},{{\hat y}_i}} \right)} \tag{6} L(Y,Y^)=i=0Nl(yi,y^i)(6)
对于分类和回归模型使用的损失函数不同,下面将分别进行介绍。



2.2 回归损失函数

(1) 均方误差损失函数

均方误差(Mean Squared Error Loss, MSE)损失函数定义如下:
(7) L ( Y , Y ^ ) = 1 N ∑ i = 1 N ( y ^ i − y i ) 2 L({\bf{Y}},{\bf{\hat Y}}) = {1 \over N}\sum\limits_{i = 1}^N {{{\left( {{{\hat y}_i} - {y_i}} \right)}^2}} \tag{7} L(Y,Y^)=N1i=1N(y^iyi)2(7)

代码示例

def mean_squared_error(y_true, y_pred):
    return np.mean(np.square(y_pred - y_true), axis=-1)

(2) 平均绝对误差损失函数

平均绝对误差(Mean Absolute Error Loss, MAE)损失函数定义如下:
(8) L ( Y , Y ^ ) = 1 N ∑ i = 1 N ∣ y ^ i − y i ∣ L({\bf{Y}},{\bf{\hat Y}}) = {1 \over N}\sum\limits_{i = 1}^N {\left| {{{\hat y}_i} - {y_i}} \right|} \tag{8} L(Y,Y^)=N1i=1Ny^iyi(8)

代码示例

def mean_absolute_error(y_true, y_pred):
    return np.mean(np.abs(y_pred - y_true), axis=-1)

(3) 均方误差对数损失函数

均方误差对数(Mean Squared Log Error Loss, MSLE)损失函数定义如下:
(9) L ( Y , Y ^ ) = 1 N ∑ i = 1 N ( log ⁡ y ^ i − log ⁡ y i ) 2 L({\bf{Y}},{\bf{\hat Y}}) = {1 \over N}\sum\limits_{i = 1}^N {{{\left( {\log {{\hat y}_i} - \log {y_i}} \right)}^2}} \tag{9} L(Y,Y^)=N1i=1N(logy^ilogyi)2(9)

代码示例

def mean_squared_logarithmic_error(y_true, y_pred):
    first_log = np.log(np.clip(y_pred, 10e-6, None) + 1.)
    second_log = np.log(np.clip(y_true, 10e-6, None) + 1.)
    return np.mean(np.square(first_log - second_log), axis=-1)

(4) 平均绝对百分比误差损失函数

平均绝对百分比(Mean Absolute Percentage Error Loss, MAPE)误差损失函数定义如下:
(10) L ( Y , Y ^ ) = 1 N ∑ i = 1 N 100 ⋅ ∣ y ^ i − y i ∣ y i L({\bf{Y}},{\bf{\hat Y}}) = {1 \over N}\sum\limits_{i = 1}^N {{{100 \cdot \left| {{{\hat y}_i} - {y_i}} \right|} \over {{y_i}}}} \tag{10} L(Y,Y^)=N1i=1Nyi100y^iyi(10)

代码示例

def mean_absolute_percentage_error(y_true, y_pred):
    diff = np.abs((y_pred - y_true) / np.clip(np.abs(y_true), 10e-6, None))
    return 100 * np.mean(diff, axis=-1)

(5) 小结

均方误差损失函数是使用最广泛的,并且在大部分情况下,均方误差有着不错的性能,因此被用作损失函数的基本衡量指标。MAE 则会比较有效地惩罚异常值,如果数据异常值较多,需要考虑使用平均绝对误差损失作为损失函数。一般情况下,为了不让数据出现太多异常值,可以对数据进行预处理操作。
均方误差对数损失与均方误差的计算过程类似,多了对每个输出数据进行对数计算,目的是缩小函数输出的范围值。平均绝对百分比误差损失则计算预测值与真实值的相对误差。均方误差对数损失与平均绝对百分 比误差损失实际上是用来处理大范围数据( [ − 10 5 , 10 5 ] \left[ { - {{10}^5},{{10}^5}} \right] [105,105] )的,但是在神经网络中,我们常把输入数据归一化到一个合理范围 ( [ − 1 , 1 ] \left[ { - 1,1} \right] [1,1] ),然后再使用均方误差或者平均绝对误差损失来计算损失。



2.3 分类损失函数

(1) Logistic损失函数

Logistic损失函数定义如下:
(11) L ( Y , Y ^ ) = ∏ i = 1 N y ^ i y i ⋅ ( 1 − y ^ i ) 1 − y i L({\bf{Y}},{\bf{\hat Y}}) = \prod\limits_{i = 1}^N {\hat y_i^{{y_i}}} \cdot {(1 - {\hat y_i})^{1 - {y_i}}} \tag{11} L(Y,Y^)=i=1Ny^iyi(1y^i)1yi(11)


(2) 负对数似然损失函数

负对数似然损失函数(Negative Log Likelihood Loss)定义如下:
(12) L ( Y , Y ^ ) = − ∑ i = 1 N y i ⋅ log ⁡ y ^ i + ( 1 − y i ) ⋅ log ⁡ ( 1 − y ^ i ) L({\bf{Y}},{\bf{\hat Y}}) = - \sum\limits_{i = 1}^N {{y_i} \cdot \log {{\hat y}_i} + (1 - {y_i}) \cdot \log (1 - {{\hat y}_i})} \tag{12} L(Y,Y^)=i=1Nyilogy^i+(1yi)log(1y^i)(12)


(3) 交叉熵损失函数

Logistic损失函数和负对数似然损失函数只能处理二分类问题,对于两个分类扩展到M个分类,使用交叉熵损失函数(Cross Entropy Loss),其定义如下:
(13) L ( Y , Y ^ ) = 1 N ∑ i = 1 N ∑ j = 1 M y i , j ⋅ log ⁡ y ^ i , j L({\bf{Y}},{\bf{\hat Y}}) = {1 \over N}\sum\limits_{i = 1}^N {\sum\limits_{j = 1}^M {{y_{i,j}}} \cdot } \log {\hat y_{i,j}} \tag{13} L(Y,Y^)=N1i=1Nj=1Myi,jlogy^i,j(13)


代码示例

def cross_entropy(y_true, y_pred):
    return -np.mean(y_true * np.log(y_pred + 10e-6))

(4) Hinge损失函数

运用 Hinge 损失的典型分类器是 SVM 算法,因为 Hinge 损失可以用来解决间隔最大化问题。当分类模型需要硬分类结果的,例如分类结果是 0 或 1 、 -1或 1 的二分类数据, Hinge 损失是最方便的选择 。Hinge 损失函数定义如下:
(14) L ( Y , Y ^ ) = 1 N ∑ i = 1 N max ⁡ ( 0 , 1 − y ^ i ⋅ y i ) L({\bf{Y}},{\bf{\hat Y}}) = {1 \over N}\sum\limits_{i = 1}^N {\max (0,1 - {{\hat y}_i} \cdot {y_i})} \tag{14} L(Y,Y^)=N1i=1Nmax(0,1y^iyi)(14)


代码示例

def hinge(y_true, y_pred):
    return np.mean(np.maximum(1. - y_true * y_pred, 0.), axis=-1)

(5) 指数损失函数

使用指数(Exponential)损失函数的典型分类器是 AdaBoost 算法,指数损失函数的定义如下:
(15) L ( Y , Y ^ ) = ∑ i = 1 N e − y ^ i ⋅ y i L({\bf{Y}},{\bf{\hat Y}}) = \sum\limits_{i = 1}^N {{e^{ - {{\hat y}_i} \cdot {y_i}}}} \tag{15} L(Y,Y^)=i=1Ney^iyi(15)


代码示例

def exponential(y_true, y_pred):
    return np.sum(np.exp(-y_true * y_pred))



2.4 神经网络中常用的损失函数

神经网络中的损失函数可以自定义,前提是需要考虑数据本身和用于求解的优化方案。换句话说,自定义损失函数需要考虑输入的数据形式和对损失函数求导的算法。自定义损失函数其实是有些难度的,在实际工程项目上,结合激活函数来选择损失函数是常见的做法,常用组合有以下 3 种 。

(1) ReLU + MSE

均方误差损失函数无法处理梯度消失问题,而使用 Leak ReLU 激活函数能够减少计算时梯度消失的问题,因此在神经网络中如果需要使用均方误差损失函数,一般采用 Leak ReLU 等可以减少梯度消失的激活函数。另外,由于均方误差具有普遍性,一般作为衡量损失值的标准,因此使用均方误差作为损失函数表现既不会太好也不至于太差。


(2) Sigmoid + Logistic

Sigmoid 函数会引起梯度消失问题:根据链式求导法,Sigmoid 函数求导后由多个[0, 1]范围的数进行连乘,如其导数形式为 ,当其中一个数很小时,连成后会无限趋近于零直至最后消失。而类 Logistic 损失函数求导时,加上对数后连乘操作转化为求和操作,在一定程度上避免了梯度消失,所以我们经常可以看到 Sigmoid 激活函数+交叉摘损失函数 的组合。


(3) Softmax + Logisitc

在数学上,Softmax 激活函数会返回输出类的互斥概率分布,也就是能把离散的输出转换为一个同分布互斥的概率,如(0.2, 0.8)。另外,Logisitc 损失函数是基于概率的最大似然估计函数而来的,因此输出概率化能够更加方便优化算法进行求导和计算,所以我们经常可以看到输出层使用 Softmax 激活函数+交叉熵损失函数 的组合。




参考资料

[1] 图解深度学习
[2] 深度学习原理与实践
[3] TensorFlow实战Google深度学习框架(第2版)

  • 13
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechArtisan6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值