神经网络算法详解 02:感知神经网络与反向传播算法(BP)

本文介绍了感知器,自适应线性单元以及误差反向传播算法相关知识。


系列文章:

  1. 【神经网络算法详解 01】-- 人工神经网络基础
  2. 【神经网络算法详解 02】 – 感知神经网络与反向传播算法(BP)
  3. 【神经网络算法详解 03】 – 竞争神经网络【SONN、SOFM、LVQ、CPN、ART】
  4. 【神经网络算法详解 04】 – 反馈神经网络 【Hopfield、DHNN、CHNN、BAM、BM、RBM】
  5. 【神经网络算法详解 05】-- 其它类型的神经网络简介【RBF NN、DNN、CNN、LSTM、RNN、AE、DBN、GAN】


1. 感知器模型

感知器(Perceptron) 】:用于线性可分模式分类的最简单的神经网络模型。由一个具有可调树突权值和偏置的神经元组成。1958年 Frank Rosenblatt 提出一种具有单层计算单元的神经网络,即为Perceptron。
在这里插入图片描述
F r a n k   R o s e n b l a t t Frank \ Rosenblatt Frank Rosenblatt

  • 本质:是一个非线性前馈网络,同层内无互联,不同层间无反馈,由下层向上层传递。
  • 其输入、输出均为离散值,神经元对输入加权求和后,由阈值函数决定其输出。
  • 感知器实际上是一个简单的单层神经网络模型。单节点感知器就是MP模型,首次提出了习的概念。

回顾一下,在 之前的文章中 提到的M-P模型的概念。命名的由来是两位创立者的名字首字母(McCulloch & W.Pits)。

M-P模型 】:是把神经元视为二值开关元件,按照不同方式组合来完成各种逻辑运算。能够构成逻辑与、非、或,理论上可以进而组成任意复杂的逻辑关系,若将M-P模型按一定方式组织起来,可以构成具有逻辑功能的神经网络。
在这里插入图片描述
罗森布拉特给出感知机一个简单直观的学习方案:给定一个有输入输出实例的训练集,感知机「学习」一个函数:对每个例子,若感知机的输出值比实例低太多,则增加它的权重,若比实例高太多,则减少它的权重;感知器是整个神经网络的基础,神经元通过响应函数确定输出,神经元之间通过仅值进行传递信息,权重的确定根据误差来进行调节,这就是学习的过程。通俗地讲,我们说训练神经网络,其实就是我们搭建的神经网络在“学习”,可以理解成学习一个映射规则,其实就是去学习权重,当我们拿新的数据通过这个映射规则会给出我们期望的预测,比如分类问题的话,这个映射规则会给出每一类的概率。


1.1 单层感知器:线性分类

单层感知器:通过计算权重和输入乘积的和 f ( x ) = s i g n ( Σ ( w ∗ x ) ) f(x)=sign(\Sigma(w*x)) f(x)=sign(Σ(wx)),根据和的正负来判断分类。
该感知器的分类逻辑:

  • 计算各输入变量加权后的和 Σ \Sigma Σ
  • 的根据和 Σ \Sigma Σ 是否大于0得到分类结果;

对于任一个训练练样本,其输入特征为 ( x i 1 , x i 2 ) (x_{i1},x_{i2}) (xi1,xi2)
求和: n e t = x 1 ∗ W 1 + x 2 ∗ W 2 + b net=x_1*W_1+x_2*W_2+b net=x1W1+x2W2+b
x 1 ∗ W 1 + x 2 ∗ W 2 + b > 0 x_1*W_1+x_2*W_2+b>0 x1W1+x2W2+b>0 时,为正类;
x 1 ∗ W 1 + x 2 ∗ W 2 + b < = 0 x_1*W_1+x_2*W_2+b<=0 x1W1+x2W2+b<=0 时,为负类;
x 1 ∗ W 1 + x 2 ∗ W 2 + b = 0 x_1*W_1+x_2*W_2+b=0 x1W1+x2W2+b=0 为正负类的分界超平面;

上式中, x 1 x_1 x1的下标“1”表示第一个样本,以此类推,将直线方程 x 1 ∗ W 1 + x 2 ∗ W 2 + b = 0 x_1*W_1+x_2*W_2+b=0 x1W1+x2W2+b=0 整理成习惯的公式: f ( x 1 ) = − W 1 W 2 x 1 − b f(x_1) = -\frac{W_1}{W_2}x_1-b f(x1)=W2W1x1b

二维空间中的分隔超平面为一条直线,直线以上的点为正类(直线(二维空间的分隔超平面)法向量的方向为正方向),直线下的点为负类。

之前的文章SVM中介绍了超平面的定义,我们再来复习一下【超平面】的相关知识:

  • 定义:指n维线性空间中维度为n-1的子空间。它可以把线性空间分割成不相交的两部分。
  • 比如,二维空间中,把平面分成了两部分的直线(一维)是超平面;三维空间中,把空间分成了两部分的平面(二维)是超平面。
  • 法向量:垂直于超平面的向量。
    在这里插入图片描述

1.2 单层感知器的学习算法

采用的是【 离散感知器算法 】:
离散感知器:
Δ W j = η [ d j − s g n ( W j T X ) ] X \Delta W_j = \eta[d_j - sgn(W^T_j X)]X ΔWj=η[djsgn(WjTX)]X
Δ w i j = η [ d j − s g n ( W j T X ) ] x i \Delta w_{ij} = \eta[d_j - sgn(W^T_j X)]x_i Δwij=η[djsgn(WjTX)]xi
上式中,第一个式子是向量形式,第二个式子是分量表示形式。
在第一个式子中, Δ W j \Delta W_j ΔWj 表示每次要更新权重的大小, η \eta η 为学习率,[ ]中的部分为学习信号,其中 d j d_j dj 表示期望的输出, s g n ( W j T X ) sgn(W^T_j X) sgn(WjTX) 表示实际的输出; X X X表示输入。

具体步骤:

  • 初始化仅值,赋予较小的非零随机数。如果输入样本线性可分,无论初始值如何取都会稳定收敛;
  • 输入样本 X , Y {X,Y} X,Y,其中 X ( i ) = ( 1 , x 1 ( i ) , x 2 ( i ) , . . . , x n ( i ) ) X^{(i)} = (1,x^{(i)}_1,x^{(i)}_2,...,x^{(i)}_n) X(i)=(1,x1(i),x2(i),...,xn(i)),i = 1,2,…,k,其中共 k k k n + 1 n+1 n+1个分量的输入,输出为 k k k m m m 个分量的向量: Y ( i ) = y 1 ( i ) , y 2 ( i ) , . . . , y m ( i ) Y^{(i)} = y^{(i)}_1,y^{(i)}_2,...,y^{(i)}_m Y(i)=y1(i),y2(i),...,ym(i),i = 1,2,…,k;其中“1”表示偏置(bias),所以输入变为 n+1维,“m”表示输出层的维度,如果是分类问题的话,m其实就是分类数,也是最后一层神经网络的输出数,如果写过这类算法的话,不难理解。
  • 计算各输出节点的实际输出:
  • 按照实际输出值和期望值之间的差更新权重: [ W j ( i ) ) ] ′ = W j ( i ) ) + η [ d j ( i ) − o j ( i ) ] X [W^{(i)}_j)]'=W^{(i)}_j) + \eta[d^{(i)}_j - o^{(i)}_j] X [Wj(i))]=Wj(i))+η[dj(i)oj(i)]X η \eta η 为学习率;
  • 返回第二步,处理下一组输入样本;
  • 循环上述过程,直到感知器对所有样本的实际输出和期望输出一致。注意,如果是线性可分的,可以实现;如果是非线性的,则不一定能保证一致。

1.3 单层感知器的例子

1.计算第一个样本,第一次更新权重。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
经过以上第一轮(1个epoch)对所有四个样本进行训练之后,发现有两个样本的期望输出(标签)与实际输出(预测值)不一致,此时,我们需要再进行下一轮的训练,如此训练多轮(多个epoch),直到找出一组合适的权重能够把所有样本的期望输出和实际输出达到一致时,训练结束。此时,这组权重就是我们可以接受的一组解。结合下图,在当前的语境下(单层感知器,线性,三维空间),这组权重可以理解为:方程的系数,线性分类器,三维空间的超平面,按照前文所讲的定义,三维空间中的超平面为一个平面。下图中,分隔超平面将红色和蓝色两类样本完全分隔开。
在这里插入图片描述

1.4 线性与非线性

线性(Linear)的严格定义是一种映射关系,其映射关系满足可加性和齐次性。通俗理解就是两个变量之间存在一次方函数关系,在平面坐标系中表现为一条直线。不满足线性即为非线性〈non-linear)。回想之前讲得买袜子的例子。

1.5 线性与非线性分类器

线性可分,即可以用一个超平面将正负样本分开。以二维空间的二分类为例,是指可以通过一条直线将数据分成正负样本两类。分类器(算法)也有线性和非线性两类。

  • 线性分类器使用超平面类型的边界,非线性分类器使用超曲面型的边界
  • 线性分类器简单、容易理解,非线性分类器能解决更复杂的分类问题
    在这里插入图片描述

1.5 单层感知器:实现逻辑运算

聪明绝顶警告!
在这里插入图片描述
1969年《Perceptrons》(感知器)证明了无法使用感知器来表示异或。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


2. 多层感知器

多层感知器(MuIti Layer Perceptron,即MLP)是一种前馈人工神经网络模型,包括至少一个隐藏层(除了一个输入层和一个输出层以外),将输入的多个数据集映射到单一的输出的数据集上。

  • 单和多是指有计算功能的节点所在的层数,输入层无计算功能,不计算在内。单层即只有输入层和输出层,多层至少包含一个藏层;
  • 单层只能学习线性函数,多层可以学习非线性函数,适用于模式识别、图像处理、函数逼近等领域。
    在这里插入图片描述
    给出一系列特征 X = ( x 1 , x 2 , . . . ) X= (x_1,x_2,...) X=(x1,x2,...)和目标Y,一个多层感知器可以以分类者回归为目的,学习到特征和目标之间的关系。简单的理解,分类输出是离散的值,比如苹果、西瓜…;回归输出是连续的值,比如预测房价等等。
    在这里插入图片描述
    加入的偏置(bias)可以理解为分类超平面的截距。
    在这里插入图片描述
    单个感知器为一个线性分类器,对应了一个分类超平面,对于二维空间,相当于一条直线,两个隐层节点对应了两条直线: − x 1 + x 2 − 0.5 = 0 , x 1 − x 2 − 0.5 = 0 -x_1+x_2-0.5 = 0, x_1- x_2-0.5 = 0 x1+x20.5=0,x1x20.5=0 在样本空间中画出两条线,可以通过一个开域(或凸域)将不同类别的样本点分开。
    在这里插入图片描述

2.1 隐藏层的效果

可以查看tensorflow的playground网站,很好的演示了隐藏层的效果。网址:http://playground.tensorflow.org/
我分别用四个隐藏层和八个隐藏层做了测试,都训练了115个epoch,其它参数配置相同,对比如下图:
在这里插入图片描述
在这里插入图片描述
可以看出隐层数目增加以后,收敛速度明显加快,损失也更小,分类效果更好(八个隐层的分类超平面更精确)。

2.2 感知器对比

在这里插入图片描述
看一下对比,下例都是两个输入 x 1 , x 2 x_1,x_2 x1,x2,两个输出,只是隐层数目不同。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. 自适应线性单元

1962年,斯坦福大学教授Widrow提出一种自适应可调的神经网络,其基本构成单元称为【自适应线性单元( Adaptive Linear Neuron)】,具主要作用是线性逼近一个函数式而进行模式联想。该模型是最早用于实际工程解决实际问题的人工神经网络。这种自适应可调的神经网络主要适用于信号处理中的自适应滤波、预测、模式识别等。主要应用于语音识别、天气预报、心电图诊断、信号处理以及系统辨识等方面。

在这里插入图片描述

3.1 ADALINE与感知器对比

在这里插入图片描述

  • 激活函数:ADALINE的激活函数为线性函数,感知器的为阈值函数;
  • 误差更新:ADALINE在输出最终结果前根据误差更新权重,感知器在输出最终结果后更新;
  • 损失函数:ADALINE使用均方误差SSE作为损失函数,可最小化损失函数,感知器没有损失函数;
  • 计算方便:凸函数,可微,有多种快速求解的方法,比如梯度下降法等;
  • 输出结果:ADALINE可以输出连续值或者分类值,感知器智能输出分类值。

3.2 回顾:最小均方学习规则

聪明绝顶警告。
在这里插入图片描述

3.3 ADALINE 例子

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
… … … … … …
在这里插入图片描述

3.4 MADALINE:多层自适应线性网络

Hoff 将 ADALINE 进行了改进和推广,提出 MADALINE 模型,即将多个 ADALINE 神经元互相连接形成一个多层网络,实际上是由 ADALINE 和 AND 逻辑器组成,可以对分线性数据进行划分。同样采取LMS的算法进行学习。但由于隐藏层的误差无法直接计算,即 LMS 算法不能直接用于含有隐藏层的神经元网络,故 MADALINE 一般不含隐藏层。对此,Widrow提出了一个需要微分的算法 MR II 用于解决多层网络的学习问题。
在这里插入图片描述


4. 误差反向传播算法

4.1 多层感知器的训练

单层感知器训练依照离散感知器学习规则或 d e l t a delta delta 学习规则,对于一组输入有一个预期输出,通过比较感知器输出结果和预期输出结果,调整感知器的权重和阈值,即可完成模型训练,得到一个比较满意的结果。

单层感知器学习规则的逻辑如下:

  • 定义一个包含感知器参数的损失函数,用来衡量当前模型优劣;
  • 调整参数的值,让损失函数逐步变小
  • 损失函数小到一定程度,使得输出结果和预期输出结果的差可以接受
  • 学习得到合乎要求的模型

伪代码:
Step1:初始化W,b
Step2:在训练据集中选取 ( X i , Y i ) (X_i,Y_i) (XiYi)
Step3:计算损失函数 L = Y i ( W X i + b ) L=Y_i(WX_i+b) L=Yi(WXi+b),如果 L lg ⁡ 0 L \lg 0 Llg0,则更新权重及偏置量: W = W + η Y i X i W=W+\eta Y_iX_i W=W+ηYiXi
step4:转至Step2


多层感知器的学习规则同样使用该逻辑,只是由于多层感知器的隐藏层的神经元,没有直接的预期输出结果,具预期输出体现在下一层(单隐藏层)或者下N层(多个隐藏层)的输出中,需要从整体考虑具体的损失函数。通俗的说就是,多层感知器权重的调整方向不像单层感知器那么明确,特别是如果有多个隐藏层的话,计算会越来越困难。没有办法定义隐藏层的损失函数,得到的损失函数是整体的损失函数。
在这里插入图片描述
下面看一个例子:
在这里插入图片描述
在这里插入图片描述
从输入建立到输出的关系,当网络结构简单时,遍历路径可控:
b ⟶ h 1 ⟶ o b \longrightarrow h_1 \longrightarrow o bh1o
b ⟶ h 2 ⟶ o b \longrightarrow h_2 \longrightarrow o bh2o
x 1 ⟶ h 1 ⟶ o x_1 \longrightarrow h_1 \longrightarrow o x1h1o
x 1 ⟶ h 2 ⟶ o x_1 \longrightarrow h_2 \longrightarrow o x1h2o
x 2 ⟶ h 1 ⟶ o x_2 \longrightarrow h_1 \longrightarrow o x2h1o
x 2 ⟶ h 2 ⟶ o x_2 \longrightarrow h_2 \longrightarrow o x2h2o

当输入变量增多、隐藏层增加或者隐藏层中的神经元增多时,求偏导的次数快速增加,与神经元个数的二次方成正比。计算量过大,使得学习过程变得仅仅理论上可行(这也是闵斯基看衰ANN的原因)。设想一个2个隐藏层,每个隐藏层10个节点的结构,输入变量 x 1 x_1 x1将会有 10 ∗ 10 10*10 1010 条路径影响到输出变量 o o o

从输入到输出的链式求导顺序,关注的是输入对输出的影响。事实上通过简单计算能得到模型输出和实际输出的差异,我们更想知道的是输出 o 的变化对前面的路径(输入+权重参数)有什么影响。换句话说就是,如果我们知道权重和输入样本,计算输出是很简单的;但是反过来,如果知道输出值和期望值的差异,那么我们应该调整权重,使得这个差异接近0,多层感知器的参数众多,使用正向求导去调整这些参数的话是很困难的,也就是很难训练,因此就要用到反向传播算法。


在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
前向和反向的特点·

  • 前向的“分母”一样,反向的“分子”一样;
  • 前向强调的是某个输入对某个神经元的影响;
  • 前向求导方式是“路径加和”,逐条路径求解;
  • 反向采用的是“合并同类路径,分阶段按层次求解”;
  • 前向会导致路径中的部分冗余遍历,反向不会;
  • 反向中某一阶段计算时可以直接使用上一阶段的结果;
  • 前向计算量比反向大很多;

4.2 BP算法

BP算法(Error Back Propagation,误差反向传播算法):是目前用来训练人工神经网络(ANN)最
常用、最有效的算法。由1974年 Paul Werbos 最早提出,未被重视,后由 Rumelhart 和 McCeIIand 发
表《并行分布式处理》正式提出。

在这里插入图片描述

4.2.1 (回顾)损失函数优化:梯度下降法

在这里插入图片描述

4.2.2 简单推导:反向传播算法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3 BP网络训练步骤

在这里插入图片描述

4.4 激活函数的特点

一个好的激活函数通常会具有的特点:

  • “非线性:导数不能是常数,否则网络会退化成单层网络;
  • “处处可微:或者说几乎处处可微,保证能计算梯度,从而进行参数优化;
  • 计算简单:降低计算难度和复杂程度;
  • “非饱和性:饱和指在某些区间内,梯度接近于零(梯度消失),使得参数无法继续更新,即无法收敛;
  • “单调性:即导数符号不变(朝一个方向变化),免梯度方向经常变化导致的不易收敛;
  • 有限的输出范围,面对不同范围的输入也会保持输出的稳定性,但有时会导致梯度消失;

4.5 误差曲面存在的问题

在这里插入图片描述

4.6 标准BP算法存在的问题

容易陷入局部最小,得不得到全局最优值饱和区导致训练次数多,收敛缓慢设计网络时,关键信息选取无明确依据。

  • 隐层数
  • 隐层节点数
  • 学习涑率
    训练顺序对训练有影响,有遗忘的特点。

4.7 标准BP算法改进

4.7.1 增加动量项

标准BP算法在更新权重时,只考虑了当前状态下的误差梯度,而未考虑之前的梯度变化情况,导致训练过程震荡,收敛过慢,为了提高训练速度,在权值调整公式中增加一项,用来保留之前的权重更新的信息,即权重更新公式变为:

Δ W ( t ) = η δ X + α W ( t − 1 ) \Delta W(t) = \eta \delta X + \alpha W(t-1) ΔW(t)=ηδX+αW(t1)

其中 α W ( t − 1 ) \alpha W(t-1) αW(t1) 被称作动量项,从前一次的权重更新的量中取一部分叠加到当前的更新量中,从而增加了之前梯度变化的影响。

  • α \alpha α 被称作动量系数,具取值一般在(0,1)之间。
  • 动量项反映了以前积累的权重调整经验,使仅重调整有了“记忆“
  • 动量项对当前调整起到了阻尼的作用。当误差变化起伏剧烈时,动量项可以减小震荡趋势,提高训练速度。

4.7.2 自适应调节学习率

标准BP算法中的 η \eta η 称作学习率、学习速率、步长等,通常定为常数,在仅重更新中起到了较大的作用,该值选择不合适,会严重影响学习过程和学习结果。

  • 结合误差曲面,当在平坦区时,希望学习率大一些,达到同样的训练效果的同时,可以减少训练次数;
  • 到当在误差变化剧烈的区域,希望学习率小一些,以免步子过大,迈过理想值,从而引起不合理的震荡;
  • 可在学习过程中适当调整学习率,方法很多,如:
    设置初始学习率
    经过一批权值调整步骤后,如果总误差上升,说明学习率大了,需要减小,则: η ( t + 1 ) = β η ( t ) \eta(t+1) = \beta \eta(t) η(t+1)=βη(t) 。如果总误差下降,说明学习率小了,可以增加,则: η ( t + 1 ) = θ η ( t ) \eta(t+1) = \theta \eta(t) η(t+1)=θη(t)

4.7.3 陡度因子

进入误差曲面的平坦区,通常是因为神经元进入了激活函数的饱和区,如果能设法在饱和区内对激活函数进行调整,比如压缩神经元的净输入,使其输出退出饱和区,就可以脱离当前的平坦区。可以在原激活函数中引入陡度因子 λ \lambda λ

o k = 1 1 + e − n e t k λ o_k = \frac {1} {1 + e^\frac {-net_k}{\lambda}} ok=1+eλnetk1

在这里插入图片描述


课程链接:https://edu.aliyun.com/course/1923

  • 11
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
全连接神经网络反向传播算法是一种用于训练神经网络算法。它是一种基于梯度下降的优化算法,通过计算代价函数的梯度来更新神经网络的权重和偏置。 下面是全连接神经网络反向传播算法的详细步骤: 1. 前向传播:将输入数据送入神经网络进行前向传播,得到输出结果。 2. 计算代价函数:计算输出结果与实际结果之间的代价函数,通常使用平方误差函数。 3. 反向传播:计算代价函数对权重和偏置的梯度,通过链式法则计算每一层的梯度值。 4. 更新权重和偏置:根据梯度下降算法的原理,更新每一层的权重和偏置。 下面是一个全连接神经网络反向传播算法的实例: 假设我们有一个三层的全连接神经网络,输入层有 2 个神经元,隐藏层有 3 个神经元,输出层有 1 个神经元。我们使用 sigmoid 函数作为激活函数,使用平方误差函数作为代价函数。 1. 前向传播:假设输入数据为 [0.5, 0.8],将其输入到神经网络中进行前向传播,得到输出结果为 0.6。 2. 计算代价函数:假设实际结果为 0.4,计算平方误差代价函数为 (0.6 - 0.4)^2 = 0.04。 3. 反向传播:计算代价函数对权重和偏置的梯度。首先计算输出层的梯度,根据链式法则,代价函数对输出层的输出值的梯度为 2 * (0.6 - 0.4) = 0.4,输出层的梯度为 0.4 * sigmoid'(0.6) = 0.14。然后计算隐藏层的梯度,隐藏层的梯度等于输出层梯度乘以输出层与隐藏层之间的权重矩阵的转置乘以隐藏层的输出值的导数,即 0.14 * W2.T * sigmoid'(0.5, 0.2) = [0.041, 0.035, 0.046]。最后计算输入层的梯度,输入层的梯度等于隐藏层梯度乘以隐藏层与输入层之间的权重矩阵的转置乘以输入层的输出值的导数,即 [0.041, 0.035, 0.046] * W1.T * sigmoid'(0.5, 0.8) = [0.0042, 0.0054]。 4. 更新权重和偏置:根据梯度下降算法的原理,更新每一层的权重和偏置。假设学习率为 0.1,更新公式为: W = W - learning_rate * dW b = b - learning_rate * db 其中,W 表示权重矩阵,b 表示偏置向量,dW 表示权重的梯度,db 表示偏置的梯度。 对于输出层与隐藏层之间的权重矩阵,更新公式为: W2 = W2 - learning_rate * (a1.T * d2) b2 = b2 - learning_rate * d2 其中,a1 表示隐藏层的输出值,d2 表示输出层的梯度。 对于隐藏层与输入层之间的权重矩阵,更新公式为: W1 = W1 - learning_rate * (x.T * d1) b1 = b1 - learning_rate * d1 其中,x 表示输入数据,d1 表示隐藏层的梯度。 以上就是全连接神经网络反向传播算法详解以及实例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

datamonday

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

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

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

打赏作者

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

抵扣说明:

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

余额充值