Transformer模型

一、Transformer前身

  • transformer的前身是[[seq2seq模型]],最初用于机器翻译,设计初衷是将一个长度可变序列(例如一个句子)作为输入,通过一个神经网络将其转换为另一个长度可变的序列(如另一个语言的翻译)。seq2seq的核心思想就在于Encoder将特征压缩为语义向量c,再通过Decoder对特征进行放大。
    ![[Pasted image 20230619221055.png]]

1.1 简单介绍seq2seq

![[Pasted image 20230619221248.png]]

[[seq2seq模型]]由两个主要部分组成:编码器Encoder和解码器Decoder

  • Encoder将输入序列转换为一个固定长度的向量,而Decoder将该向量生成输出序列

  • 核心思想:将整个序列(输入)encode为一个向量,然后使用该向量来生成(decode)另一个序列(输出)

  • 整体上来说,seq2seq是一种先降维后升维的方法

  • Encoder和Decoder通常使用循环神经网络RNN或变体,例如[[LSTM以及GRU模型]]

  • Encoder的输入是一次性全部输入的,而Decoder的输入是按照时间序列依次输入(Decoder上一轮的输出作为本轮的输入)

  • 主要缺点

    • 梯度爆炸和梯度消失问题(每个时间步的隐藏状态必须传递给下一个时间步,长序列场景下前期内容的隐藏状态会湮灭或爆炸)
    • 由于时间步的存在,模型训练无法并行执行,效率低下

二、Transformer模型架构

Transformer是seq2seq的升级,所以自然也是Encoder-Decoder架构。
Transformer模型详解

2.1 整体架构

![[Pasted image 20230619222841.png]]

  1. 输入嵌入层 Input Embedding Layer
  2. 位置编码层 Positional Encoding Layer:[[Attention机制]]相比[[RNN模型]]提取到了更多的information,但是丢失了部分RNN的语序表达,而Positional Encoding Layer为其补全了丢失的语序表达。
  3. 编码器 Encoder:内有多层Encoder Layer
  4. 解码器 Decoder:内有多层Decoder Layer
  5. 输出层 Output Layer:由Dense层Linear和Softmax组成,将Decoder的结果化为一维,并求得概率分布进行输出。

2.2 输入嵌入层和位置编码层

![[Pasted image 20230619225912.png]]

  • 输入嵌入层将输入序列(一维)中的每个元素转化为d维向量表示,通常这一过程通过词嵌入Word Embedding实现
  • Input Embedding和Output Embedding的联系:二者都是通过token embedding matrix得到的,需要先构建token embedding matrix,维度是vocab_size * d_model(vocab——size是词汇量的大小,d_model是词向量的长度),然后用tf.nn.embeddinglookup函数在matrix里按照x查询得到input embedding,x是由词的id构成的向量,设x的长度为T1,那么最终得到的input embedding的维数就是T1 * d_model。output embedding和input embedding的得到方式相同,不同的地方在于把x换成decoder_inputs,以机器翻译为例,要把T1长的英文翻译成T2长的中文,那么x是T1长的英文词id,decoder inputs就是对应T2长的中文词id,所以最终得到的output embedding是T2乘d_model维。这里可以看出,机器翻译的本质是学习不同语言间词的id的对应情况。
  • 位置编码层将会捕捉输入序列中元素之间的位置信息,同样通过d维向量表示,并与输入嵌入层的结果相加
  • 因为 Transformer 不采用 RNN 的结构,而是使用全局信息,不能利用单词的顺序信息,而这部分信息对于 NLP 来说非常重要。

2.3 输出层

![[Pasted image 20230619225947.png]]

  • 输出层通过一个线性全连接层将解码器的输出映射到目标语言词汇表中,并使用Softmax得到预测结果各种词的概率分布,来推断输出内容

2.4 Encoder层和Decoder层

![[Pasted image 20230619230316.png]]

  • Encoder由n层Encoder Layer组成
  • 每个Encoder Layer由两个子层组成
    • 多头自注意力层 Multi-Head Attention:计算输入information之间的相关性
    • 前向传播层(全连接层) Feed Forward NN

CNN基础知识——全连接层(Fully Connected Layer)

  • Add & Norm称为残差:残差网络可以更好地拟合分类函数,以获得更高的分类精度,在后文中有介绍,不妨先往下看看。
    ![[Pasted image 20230619231409.png]]

  • Decoder由n层Decoder Layer组成

  • 每层Decoder Layer由三个字层组成

    • 掩码多头自注意力层 Masked Multi-Head Attention:由于Encoder中输入为一次性输入,所以进入Decoder中实际为所有信息,所以需要根据时间步对未来信息进行遮掩,这样计算得来的结果较为真实,否则已知结果进行推测计算的结果不具有有效性。
    • 多头编码-解码自注意力层 Encoder-Decoder Multi-Head Attention
    • 前向传播层 Feed Forward NN

三、词嵌入与位置嵌入

众所周知,神经网络的输入和输出都是向量,为了让语言模型能够被神经网络处理,我们必须把文本序列表达为向量的形式,这样才能被神经网络处理,所以Transformer中我们第一步就是要将输入信息embedding成向量。

3.1 Transformer中的Tokenizer

由于Transformer的输入是文本序列,我们需要用下面的两个步骤对输入进行向量化(基于BPE的Tokenizer)

  • 使用word embedding技术对输入文本进行初步的向量化表示,这里可以用word2vec也可以使用glove
  • 使用Positional Encoding技术对输入文本的词语位置进行向量化表示,确保词语在文本序列中的绝对位置相对位置得到表达。
    ![[Pasted image 20230619232334.png]]

3.2 BERT中的Tokenizer

3.2.1 主要部分

[[BERT模型]]的Tokenizer主要分为三个部分

  1. Basic Tokenizer
  2. word Piece Tokenizer
  3. Full Tokenizer = BT+ WT
  4. BT:输入文本转成Unicode->去除各种奇怪字符->处理中文/英文->空格分词->去除多余字符和标点分词->再次空格分词
  5. WT采用了PBE的思路进行子词级别的分词(这一步对中文无效,英文中的词汇存在词根和词缀概念,所以更需要进行分词处理)

3.2.2 BPE

  • Original BPE
    ![[Pasted image 20230620111454.png]]

  • Expanding the vocabulary using BPE

    • Start with a vocabulary of characters
    • Most frequent N-Gram pairs -> a new N-Gram
      ![[Pasted image 20230620111658.png]]
  • 实质:找句子中重复最多的type-pair,放入vocab词典

  • 上图中be,st重复最多,所以vocab词典更新为{b,e,d,t,r,s,w,st,be}

  • 重复这个过程直到无type-pair可差

3.2.3 Word Piece

[[BERT模型]]采用的是BPE微变体WordPiece

WordPiece与BPE主要区别就在于它会为每个子词添加一个特殊的前缀(如’##‘)以便更好处理词汇表中未出现的单词,从而提高其泛化能力

例如,在使用WordPiece对文本进行编码时
- 一个不在词汇表中的单词’openhanded‘
- 可能会被拆分为多个子词
- 如’open‘,’##hand‘和’##ed‘
这样即使’openhanded‘不在词汇表中,BERT模型仍可以通过这些子词来理解和处理它

举个例子,假设有个单词’unstoppable‘
- 在BPE编码中,可能会被分解为’un‘,’st‘,’op‘,’able‘等子词
- 而在WordPiece编码中,可能会分解为’un‘,’##st‘,’##op‘和’##able‘
- 这样可以更好地区分不同子词在原单词中的位置

3.2.4 BPE具体操作步骤和用途

  • BPE具体操作步骤

    1. 统计原始文本中所有字符频率
    2. 将最频繁出现的字符对合并为一个新的符号
    3. 重复第二步,知道达到预设的词汇表大小
  • BPE用途

    1. BPE可以有效地缩减词汇表大小,减少模型复杂性
    2. 同时,由于合并的是频繁出现的字符对,BPE还可以在一定程度上捕捉词汇中的结构信息
      ![[Pasted image 20230620162317.png]]

3.3 位置嵌入 Positional Embedding

由于Word Embedding层输出的向量维度是d_model维,那么Positional Encoding层的输出向量也需要是d_model维,具备相同维度才能将两个向量拼接在一起,提供给神经网络。
![[Pasted image 20230620162640.png]]

对于文本序列’我爱北京天安门‘,对其进行位置编码操作
- PE(pos, 2i)中PE代表位置编码函数,用于生成位置编码向量,pos代表当前文本在序列中的位置(绝对位置),2i代表位置编码向量中的序列下标,d_model代表位置向量的总长度(总维度)
- 每个位置的序列文本的位置编码向量都由sin和cos函数表达
- 位置向量需要表达文本在序列中的绝对位置和相对位置 P E ( p o s , 2 i ) = s i n ( p o s 1000 0 2 i d m o d e l ) P E ( p o s , 2 i + 1 ) = c o s ( p o s 1000 0 2 i d m o d e l ) \begin{aligned} PE(pos,2i)&=sin\big(\frac{pos}{10000^{\frac{2i}{d_{model}}}}\big) \\ PE(pos,2i+1)&=cos\big( \frac{pos}{10000^{\frac{2i}{d_{model}}}} \big) \end{aligned} PE(pos,2i)PE(pos,2i+1)=sin(10000dmodel2ipos)=cos(10000dmodel2ipos)
![[Pasted image 20230620163607.png]]
![[Pasted image 20230620164111.png]]

  • 相对位置的表达:需要建立一个函数关系 f ( x + y ) = f ( x )   f ( y ) f(x+y)=f(x)~f(y) f(x+y)=f(x) f(y) 即可
    • 作为位置编码,PE(x+y)与PE(x)和PE(y)存在函数表达关系则说明三者之间存在相互的关联,关联的关系则可以表达出相对的位置关系。
    • 借助上述公式,我们可以得到一个特定位置的 d m o d e l d_{model} dmodel 维的位置向量,并且借助三角函数的性质 { s i n ( α + β ) = s i n α c o s β + c o s α s i n β c o s ( α + β ) = c o s α c o s β − s i n α s i n β \left\{ \begin{aligned} sin(\alpha+\beta)&=sin\alpha cos\beta+cos\alpha sin\beta \\ cos(\alpha+\beta)&=cos\alpha cos\beta-sin\alpha sin\beta\\ \end{aligned} \right. {sin(α+β)cos(α+β)=sinαcosβ+cosαsinβ=cosαcosβsinαsinβ
    • 可以得到 { P E ( p o s + k , 2 i ) = P E ( p o s , 2 i ) ⋅ P E ( k , 2 i + 1 ) + P E ( p o s , 2 i + 1 ) ⋅ P E ( k , 2 i ) P E ( p o s + k , 2 i + 1 ) = P E ( p o s , 2 i + 1 ) ⋅ P E ( k , 2 i + 1 ) − P E ( p o s , 2 i ) ⋅ P E ( k , 2 i ) \left\{ \begin{aligned} PE(pos+k,2i)&=PE(pos,2i)·PE(k,2i+1)+PE(pos,2i+1)·PE(k,2i)\\ PE(pos+k,2i+1)&=PE(pos,2i+1)·PE(k,2i+1)-PE(pos,2i)·PE(k,2i) \end{aligned} \right. {PE(pos+k,2i)PE(pos+k,2i+1)=PE(pos,2i)PE(k,2i+1)+PE(pos,2i+1)PE(k,2i)=PE(pos,2i+1)PE(k,2i+1)PE(pos,2i)PE(k,2i)也就是2i代表的是sin,2i+1代表cos,pos+k表示自变量
    • 可以看出,对于pos+k位置的位置向量某一维2i或2i+1而言,可以表示为pos位置与k位置的位置向量2i与2i+1的线性组合,这样的线性组合意味着位置向量中蕴含了相对位置信息。
  • 绝对位置表达:需要确保每一个pos的位置嵌入向量均唯一

3.4 文本向量进入Encoder-Decoder

  • Word Embedding层与Positional Encoding层的输出,作为最底层Encoder/Decoder层的输入
  • 经过Multi-Self-Attention层得到特征结果 Z t Z_t Zt ,再输入FFNN(Feed Forward Neural Network)得到特征结果 r t r_t rt
  • r t r_t rt 作为Encoder/Decoder的输出,成为了下一层Encoder/Decoder Layer 的输入
    ![[Pasted image 20230620174355.png]]

四、Attention

变形金刚(Transformer) - 魔法学院小学弟Transformer中的Attention可以借鉴一下Arwin老师的博客,比我写的好多了。

4.1 Self Attention

RNN中的Self-Attention
![[Pasted image 20230620174530.png]]

Self-Attention中的QKV方法跟[[Attention机制]]中self-attention相同。

4.1.1 Self-Attention中的QKV方法

以单头注意力机制为例
![[Pasted image 20230620175158.png]]

  • 输入的文本通过词嵌入和位置嵌入转化为向量X1,X2

  • 使用三个可训练矩阵 W Q 、 W K 、 W V W_Q、W_K、W_V WQWKWV 分别与X1、X2进行矩阵相乘,分别得到q1,q2,k1,k2,v1,v2,这里的q1,q2是指不同的X在同一Query下的映射,而不是指多头注意力中的多个Query

  • 这里可以理解为Query(需要查询的特征),Key(当前的关键特征),Value(蕴含的信息)。可以理解为评价标准为Query,现有n个例子(n个key),对应的具体情况为m个value,注意力机制就是找到一个函数,去评估n个key中最符合query要求的例子。
    ![[Pasted image 20230620180540.png]]

  • 在得到Xi的三个特征向量qi,kj,vj 后,开始计算注意力权重得分

  • 使用 s c o r e = q i ∗ k j score = q_i*k_j score=qikj ,即qi和kj向量的点乘作为得分结果,我们可以回顾一下点乘的公式 a ⃗ ∗ b ⃗ = ∣ a ⃗ ∣ ∗ ∣ b ⃗ ∣ ∗ c o s θ \vec{a}*\vec{b}=|\vec{a}|*|\vec{b}|*cos\theta a b =a b cosθ ,其意义就是比较两个向量的相关程度,越相关(方向越接近),其得分值也就越大。

  • 权重得分需要得到的是选定位置的文本与其他位置的文本的相关性得分,因此使用唯一的Query与多个Key的方式进行
    ![[Pasted image 20230620180949.png]]

  • 将上一步计算得到的分数除以 d m o d e l \sqrt{d_{model}} dmodel ,这里参数并不是唯一的

  • 由于Q和K的维度可能很大,因此需要将其除以 d k \sqrt{d_k} dk ​​来缩放。这有助于避免在softmax计算时出现梯度消失或梯度爆炸的问题,目的是为了在训练时获得更稳定的梯度

  • 将结果传给Softmax函数进行归一化,得到更清晰的相关性得分
    ![[Pasted image 20230620181126.png]]

  • 将Softmax得到的分数与特征向量 V t V_t Vt 进行相乘,得到 V t ′ V_t' Vt ,这里 V t V_t Vt 和Softmax分数相乘也就是所有词和主体词的加权求和。这会产生自注意层的输出,例如,词“Thinking”经过self-attention处理后的输出为 0.88 * v1 + 0.12 * v2 ,即当前这句话经过self-attention处理后,词“Thinking”的含义包含了88%的自身含义和12%的下一词"Machines"的含义,这样处理就体现了文本上下文的关系。当前这句话中的其他词也要做相同的处理。

  • 对所有 V t ′ V_t' Vt 求和,对于唯一的Query得到Self-Attention层的输出Z
    Z = ∑ i V i = ∑ i s o f t m a x ( Q ∗ K i T d k ) ⋅ V i Z=\sum_i V_i=\sum_isoftmax(\frac{Q*K_i^T}{\sqrt{d_k}})·V_i Z=iVi=isoftmax(dk QKiT)Vi
    ![[Pasted image 20230620182811.png]]

4.2.2 Multi-Head Self-Attention

多头的计算方式和单头完全相同
使用多头可以帮助模型增加“表示子空间” 的能力

从单头注意力转换到多头注意力:原先单头注意力机制的Q权重矩阵 W Q n \frac{W_Q} n nWQ ,头数为n,在不同头数(Query)下进行计算,即转换为多头注意力机制。

头数是一个超参,最后将不同维度的结果进行综合考虑。

在这里插入图片描述

  • 当存在8个Self-Attention头时,将会产生8个特征输出向量,但是FFNN层的输入仅为一个向量而非8个

[Pasted image 20230620183239.png
![[Pasted image 20230620183247.png]]

  • 将8个头产生的结果链接起来产生新的特征向量 Z ′ Z' Z ,将Z与可训练特征向量 W o W_o Wo (额外参数层)相乘,最终得到输出结果Z

4.3.3 总结

![[Pasted image 20230620183432.png]]

  • 将输入文本通过词嵌入和位置嵌入转化为文本向量
  • 除了最底层的encoder/decoder接受文本向量,其余都接受下一层的输出作为输入
  • 使用多头分别计算attention的结果
  • 将多头的结果通过特征矩阵Wo进行计算,得到多头注意力层的结果Z

4.2 Attention Mask

  • 应用场景:
    1. Encoder Self Attention
    2. Encoder-Decoder self attention
      • 因为输入文本的长度不可能永久保持一致,因此在预处理过程中需要padding操作
      • Masking在此时会将句子中padding的部分权重置0,确保不会影响注意力得分
    3. Decoder Self Attention
      • 由于在decoder中使用masking是为了避免decoder在预测下一个单词时窥视后文内容,因此每一轮训练时,对 Q ⋅ K T Q·K^T QKT 的结果都需要加上Masking Vector,以保证当前时刻只能看到当前时间步及以前的内容,对后文信息需要mask处理掉。

![[Pasted image 20230620231639.png]]

  1. 一个长度为2的序列文本
  2. 计算Decoder Attention得分场景
  3. 在计算第一个词的attention得分时,不应当参考第二个词的存在
  4. 在计算第二个词的attention得分时,不应当参考第三个词的存在(假设)
    由于Encoder部分是一口气全部输入,而Encoder结果又作为输入直接传给Decoder部分,所以存在如若不加mask的话Decoder可以直接看到后文内容进行预测,这样容易造成模型的过拟合,所以需要增加mask部分对未来内容进行mask防止这种情况出现。

4.3 Encoder-Decoder Attention

![[Pasted image 20230620232325.png]]

  1. Encoder-Decoder Attention层如上所示
  2. 分别使用Encoder层的输入和Lower Level Decoder’s Self Attention的输出作为输入。Encoder output给每一层Decoder。Decoder输入为Encoder output和上一层Decoder output
  3. lower attention的输出与Query特征矩阵进行操作
  4. Encoder的输出与Value/Key特征矩阵进行操作
  5. 目的是为了获取原文本中的特征信息

五、Encoder-Decoder Layer

5.1 Residuals 残差链接

每个Encoder/Decoder的子层内部都会接上两个残差链接,通过对Self-Attention的输出和输入layer Normalize生成结果 Z t Z_t Zt 。残差连接可以帮助梯度的反向传播,让模型更快更好的收敛。层归一化用于稳定网络,减轻深度学习模型数值传递不稳定的问题。
resnet中的残差连接
![[Pasted image 20230620232922.png]]

5.2 Feed-Forward Neural Network 前馈神经网络FNN

具体操作:
1. 对输入进行一次线性变换,并加入bias1
2. 对step1的结果进行Relu激活
3. 对step2的结果进行第二次线性变化,并加入bias2
F F N N ( x ) = W 2 R e l u ( W 1 x + b 1 ) + b 2 = m a x ( 0 , W 1 x + b 1 ) W 2 + b 2 FFNN(x)=W_2Relu(W_1x+b_1)+b_2=max(0,W_1x+b_1)W_2+b_2 FFNN(x)=W2Relu(W1x+b1)+b2=max(0,W1x+b1)W2+b2
前馈网络本质上就是几层神经网络层,中间有 ReLU 激活,两层之间也有残差链接。Transformer 中的 FNN(Feed-Forward Neural Network)是一个MLP,它在自注意力机制之后对序列中的每个向量单独应用。FNN 起到两个主要作用:

  1. 引入非线性:虽然自注意力机制能捕捉序列中不同位置的向量之间的依赖关系,但它本质上是线性的。通过引入 FNN 层,Transformer 可以学习到输入序列的非线性表示,这有助于模型捕捉更复杂的模式和结构

  2. 局部特征整合:FNN 层是一个MLP,对序列中每个位置的向量独立作用。这意味着它可以学习到局部特征并整合这些特征,以形成更丰富的特征表示。这种局部特征整合与自注意力机制中的全局依赖关系形成互补,有助于提高模型性能。换句话说,自注意力机制学习的是向量之间的关系,而FNN学习的是每个向量本身更好的特征表示

MLP 多层感知机
神经网络1:多层感知器-MLP - 知乎 (zhihu.com)

Encoder-Decoder内部的那些结构self-attention,attention mask这些在Attention中已经具体介绍了,所以其实Encoder-Decoder Layer也介绍的差不多了。

六、Linear-Softmax Layer

![[Pasted image 20230620235801.png]]

  1. 将decoder层的结果通过线性层转换为字典长度的一维向量
  2. 对向量进行Softmax归一化
  3. 基于Softmax的结果获得对应预测文本的概率并输出预测文本

七、总结

7.1 Transformer具体流程

![[aa0b6939f430a48c15b7e78219eaac0.jpg]]

  1. 文本序列转化为文本向量

  2. 文本向量经过Encoder提取特征生成输出向量

  3. 输出向量提供给decoder

  4. 确保decoder生成序列过程中参考输入的文本序列信息
    ![[Pasted image 20230621000214.png]]

  5. decoder的输入总start sign开始

  6. decoder的输出到end sign结束

  7. 过程中,decoder每一轮输出都会作为下一轮decoder的输入

7.2 主要组成部分和特点

  1. 编码器和解码器结构:Transformer 模型由编码器(Encoder)和解码器(Decoder)组成,它们都包含多层(通常为 6 层或 12 层)。编码器负责对输入序列进行特征提取,解码器则利用编码器的输出逐步生成输出序列。

  2. 自注意力(Self-attention)机制:自注意力机制是 Transformer 的核心组成部分。通过计算输入序列中每个元素与其他元素的关系,自注意力机制可以捕捉长距离依赖关系。自注意力计算涉及 Query(查询)、Key(键)和 Value(值)的概念。

  3. 多头注意力(Multi-head Attention):Transformer 使用多头注意力结构,允许模型同时关注输入序列中的不同位置的信息。多头注意力可以捕捉更丰富的语义信息和不同的依赖关系。

  4. 位置编码(Positional Encoding):由于自注意力机制是一种全局操作,Transformer 模型无法捕捉输入序列中的顺序信息。为解决这个问题,Transformer 引入了位置编码,将位置信息添加到输入向量中。

  5. 层归一化(Layer Normalization)和残差连接(Residual Connection):为了提高模型的训练稳定性和收敛速度,Transformer 使用了层归一化和残差连接。这些结构有助于处理梯度消失和梯度爆炸问题。

  6. 前馈神经网络(Feed-Forward Neural Network, FFNN):在自注意力机制之后,Transformer 的每层还包含一个前馈神经网络。这个小型网络包含两个线性层和一个激活函数(如 ReLU 或 GELU)。

  7. 掩码机制(Masking):Transformer 使用填充掩码(Padding Mask)忽略填充符号的影响,使用序列掩码(Sequence Mask)确保解码器在生成过程中遵循自回归原则。

最后,Transformer算法的核心是多头自注意力机制。在之后的模型研究中被广泛使用。此外,我们常说的Transformer Block其实指的是以下结构:

[Transformer Block

它可以被看作一个特征提取器,其主要目的是对输入信息的各个部分通过注意力机制进行相对整体的重要程度的重分配。

总之,Transformer 模型是一种具有自注意力机制、多头注意力结构和位置编码的深度学习模型,适用于处理序列数据。它的编码器-解码器结构、掩码机制、层归一化和残差连接等特点使得模型在自然语言处理任务中表现出色。自 Transformer 模型问世以来,它已经成为了许多先进模型(如 BERT、GPT、ChatCPT 等)的基础,并在各种 NLP 任务中取得了显著的成功。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

安徒生在ACL讲一千零一夜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值