从Seq2Seq,Attention,Transformer到ELMo,BERT,GPT-2(一)

 

一图了解从 Seq2Seq,Attention,Transformer 到 ELMo,BERT,GPT-2 的发展过程。

一、Seq2Seq

Seq2Seq全称Sequence to Sequence,结构为RNN Encoder-Decoder,Encoder将变长源序列映射为定长向量,Decoder将该向量映射回变长目标序列。论文《Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation》首次提出的GRU结构,比LSTM参数更少,更不容易过拟合。

1、Encoder 顺序读取输入序列 x=(x_{1}, ..., x_{T}),每个时刻的隐藏状态为 \mathbf{h}_{\left \langle t \right \rangle}=f(\mathbf{h}_{\left \langle t-1 \right \rangle}, x_{t}),读到<EOS>后得到整个序列的定长语义向量c。

2、Decoder 中t时刻的隐状态为 \mathbf{h}_{\left \langle t \right \rangle}=f(\mathbf{h}_{\left \langle t-1 \right \rangle}, y_{t-1}, \mathbf{c}),对应输出的条件概率为 P(y_{t}|y_{t-1}, ..., y_{1}, \mathbf{c})=g(\mathbf{h}_{\left \langle t \right \rangle}, y_{t-1}, \mathbf{c})

3、联合训练 Encoder-Decoder,令 θ 为模型参数,(x_{n},y_{n}) 为输入序列和输出序列对,目标函数是使所有序列的条件概率最大。

        \underset{\theta}{\max} \frac{1}{N} \sum _{n=1}^{N}\log p_{\theta }(\mathbf{y}_{n}|\mathbf{x}_{n})

Seq2Seq结构如图所示,左图的隐藏单元使用GRU,右图使用LSTM《Sequence to Sequence Learning with Neural Networks》,且LSTM以相反的方式读取输入语句,这样会在数据中引入许多短期依赖,从而使优化问题变得更加容易。Note that the LSTM reads the input sentence in reverse, because doing so introduces many short term dependencies in the data that make the optimization problem much easier.

Seq2Seq的几种常见架构及结果对比

 

Seq2Seq 的应用场景有:机器翻译、对话机器人、文本摘要生成、图片描述生成、诗词生成、生成代码补全、故事风格改写等。

BLEU score:评估机器翻译的质量

全称是 Bilingual Evaluation Understudy 双语评估候补,评估时观察生成的(1~n个)词是否至少出现在一个参考翻译Candidate之中(Candidates 会包含在开发集或测试集内),并把词数上限设为它在出现次数最多的某条参考翻译中的计数,在达到上限时截断计数 Countclip。还要引入一个简短惩罚因子 brevity penalty BP,防止较短的翻译取得很高的分数。令c为候选翻译的长度,r为参考翻译的长度,通常取N=4,并统一权重值 wn=1/N,则 n-gram 的 bleu score 为:

        p_{n}=\frac{\sum_{C \in \left \{ \mathrm{Candidates }\right \}}\sum_{\mathrm{n-gram} \in C}Count_{clip}(\mathrm{n-gram})}{\sum_{C' \in \left \{ \mathrm{Candidates }\right \}}\sum_{\mathrm{n- gram}' \in C'}Count(\mathrm{n-gram}')}

        \mathrm{BP}=\left\{\begin{matrix} 1 & \mathrm{if }\; c>r\\ e^{(1-r/c)} & \mathrm{if} \; c\leqslant r \end{matrix}\right.

        \mathrm{BLEU}=\mathrm{BP}\cdot \exp \left (\sum_{n=1}^{N}w_{n}\log p_{n} \right )

        \log \mathrm{BLEU}=\min(1-r/c,\: 0)+\sum_{n=1}^{N}w_{n}\log p_{n}

 

二、Attentional mechanism

1、《NEURAL MACHINE TRANSLATION BY JOINTLY LEARNING TO ALIGN AND TRANSLATE

由于 basic RNN Encoder-Decoder 将源序列信息压缩成固定长度的向量,因此难以处理长句子,尤其是对于长度超过训练集语料的输入序列。随着输入序列长度的增加,基本模型的性能将会迅速下降。因此论文提出了扩展的 Encoder-Decoder 模型 which learns to align and translate jointly,每次生成一个单词时,(soft-)搜索源句集中了最相关信息的一组位置,将注意力集中在源句的某个或某几个词上面,让它们与待生成词(soft-)对齐,使翻译更加精准。并且不将源句编码为固定长度的向量,而是将其编码成一系列的上下文向量,在解码时自适应地选择这些向量的子集,可以更好地处理长句子。

Encoder 使用双向RNN,每个时刻的 annotation h_{j} 由正向和反向两部分拼接而成,其中 \circ 为 element-wise 对应位置元素相乘,\overline{E} 为词嵌入矩阵。

Decoder 每个时刻输出的条件概率由上下文向量 c_{i}、隐藏层状态 s_{i} 和上一时刻的 y_{i} 三者的一个非线性函数得出。上下文向量 c_{i} 依赖于 Encoder 映射输入序列得到的一系列 annotations (h_{1}, ..., h_{T_{x}}) -- 加权 \alpha _{ij} 求和;每个 annotation h_{j} 都包含了关于整个输入序列的信息,并着重于聚焦于输入序列的第j个单词的周围部分。其中energy e_{ij} 是alignment model,衡量 j 周围的inputs与 i 处 output 的匹配程度,由于 U_{a}h_{j} 不依赖于Decoder的时刻 i,可以预先计算好以减少计算花费。\alpha _{ij} 是目标词 y_{i} 与源词 x_{i} soft-对齐(翻译自 x_{i})的概率。\alpha _{ij}e_{ij} 反映了 h_{j}s_{i-1} 对生成 s_{i}y_{i} 的重要程度。z_{i} 为更新门的输出,\tilde{s}_{i} 为建议的更新状态,r_{i} 为reset门的输出,E为目标语言的词嵌入矩阵。最后使用单个maxout隐藏层进行deep output。

def attention(self, prev_state, enc_outputs):
    # param prev_state: the decoder hidden state at time i-1
    # param enc_outputs: the encoder outputs, a length T list
    e_i = []
    c_i = []
    for output in enc_outputs:
        atten_hidden = tf.tanh(tf.add(tf.matmul(prev_state, self.attention_W), 
                                      tf.matmul(output, self.attention_U)))
        e_i_j = tf.matmul(atten_hidden, self.attention_V)
        e_i.append(e_i_j)
    e_i = tf.concat(e_i, axis=1)
    alpha_i = tf.nn.softmax(e_i)
    alpha_i = tf.split(alpha_i, self.num_steps, 1)
    for alpha_i_j, output in zip(alpha_i, enc_outputs):
        c_i_j =  tf.multiply(alpha_i_j, output)
        c_i.append(c_i_j)
        c_i = tf.reshape(tf.concat(c_i, axis=1), [-1, self.num_steps, self.hidden_dim * 2])
        c_i = tf.reduce_sum(c_i, 1)
        return c_i

def decode(self, cell, init_state, encoder_outputs, loop_function=None):
    outputs = []
    prev = None
    state = init_state
    for i, inp in enumerate(self.decoder_inputs_emb):
        c_i = self.attention(state, encoder_outputs)
        inp = tf.concat([inp, c_i], axis=1)
        output, state = cell(inp, state)
        outputs.append(output)
        if loop_function is not None:
            prev = output
    return outputs

2、《Effective Approaches to Attention-based Neural Machine Translation

论文测试了两种基于 attention 机制的NMT模型 -- global & local,二者的共同之处在于,在 decode 的每个时刻 t,都先将stack LSTM顶层的隐藏状态 h_{t} 作为输入,结合上下文向量 c_{t} 得到attentional hidden state \tilde{h}_{t},然后通过softmax层生成预测分布 \tilde{h}_{t}=\tanh (W_{c}[c_{t};h_{t}])p(y_{t}|y_{<t},x)=\softmax (W_{s}\tilde{h}_{t})

(1)Global Attention

在生成上下文向量 c_{t} 时需要考虑Encoder的所有隐藏状态,通过比较当前目标隐状态 h_{t} 和每个源隐状态 \bar{h}_{s} 可以得到一个变长对齐向量 a_{t}

        a_{t}(s)=\mathrm{align }(h_{t},\bar{ h}_{s})=\frac{\exp(\mathrm{score}(h_{t},\bar{h}_{s}))}{\sum _{s'}\exp(\mathrm{score}(h_{t}, \bar{h}_{s'}))}

score为基于内容的函数,有三种选择:

        \mathrm{score }(h_{t}, \bar{h}_{s})=\left\{\begin{matrix} h_{t}^{\mathrm{T}}\bar{h}_{s} & dot\\\\ h_{t}^{\mathrm{T}}W_{a}\bar{h}_{s} & general\\\\ v_{a}^{\mathrm{T}}\tanh (W_{a}[h_{t}; \bar{h}_{s}]) & concat \end{matrix}\right.

这篇论文对上一篇论文的模型进行了简化和泛化:a. 只使用 Encoder、Decoder LSTM顶层的隐藏状态。b. 计算过程更简单:h_{t}\rightarrow a_{t}\rightarrow c_{t}\rightarrow \tilde{h}_{t},然后做出预测;而上一篇论文的过程是 h_{t-1}\rightarrow a_{t}\rightarrow c_{t}\rightarrow h_{t},在预测之前要经过一个deep-output层和一个maxout层。c. 上一篇论文只实验了concat对齐函数,然而本文证明了另一种对齐函数效果更好。

 

(2)Local Attention

Global Attention 的缺点在于,它需要关注所有的源词,而且难以翻译较长的序列。为此引入局部attention机制,使每个目标词选择只关注源词的一小部分。此方法受Xu et al. (2015) soft & hard attention之间的权衡启发,比全局/soft attention计算花销更小、比hard attention更容易训练。

Soft Attention:即全局attention,每步关注输入序列的所有隐藏状态加权平均,可微 -- BP求解;

Hard Attention:每步只关注一个隐藏状态,不可微 -- 蒙特卡罗抽样、强化学习方法等。

 

Local Attention 有选择性地关注一个小的上下文窗口,在 t 时刻为每个目标单词生成一个对齐位置 p_{t},通过 [p_{t}-D,p_{t}+D] 窗口中的源隐藏状态集合的加权平均计算得到上下文向量 c_{t},D根据经验选择。与全局方法不同,局部对齐向量 a_{t} \in \mathbb{R}^{2D+1} 是固定维度的。局部attention可分为两类:

Monotonic alignment(local-m):设 p_{t}=t,假定源序列与目标序列为单调对齐;

Predictive alignment(local-p):根据公式 p_{t}=S\cdot \mathrm{sigmoid}(v_{p}^{\mathrm{T}}\tanh(W_{p}h_{t})) 预测对齐位置,其中 W_{p}v_{p} 为模型参数,S为源句子长度。为了提高 p_{t} 周围的对齐效果,以 p_{t} 为中心的高斯分布计算对齐权重,标准差σ根据经验设为D/2:

        a_{t}(s)=\mathrm{align}(h_{t},\bar{h}_{s})\exp (-\frac{(s-p_{t})^{2}}{2\sigma ^{2}})

在TensorFlow 中,Attention 的实现代码在 tensorflow/contrib/seq2seq/python/ops/attention_wrapper.py,这里面实现了 BahdanauAttention 和 LuongAttention,详见 https://blog.csdn.net/yiyele/article/details/81393229

 

三、ConvS2S

论文《Convolutional Sequence to Sequence Learning》提出了一种完全基于CNN的、带有GLU和残差连接的模型结构,与RNN模型相比,训练时所有元素的计算可以完全并行化、易于优化。使用门控线性单元简化梯度传播,并为每个 Encoder 层配备了单独的注意力模块。

多层CNN创建分层的输入表征,近处的元素在较低层相互作用,而远处的元素在较高层相互作用。对于长度为n的句子,分层结构提供了捕获长期依赖的较短路径,使用宽度为k的 filter 仅需 O(n/k) 次卷积运算,而链式结构的RNN需要计算 O(n) 次。

输入元素由词向量 X \in \mathbb{R}^{k*d} 和位置信息向量结合而成 e=(w_{1}+p_{1}, ..., w_{m}+p_{m}),Decoder 中的输出也做类似处理。Encoder 和Decoder 网络共享一个简单的块结构,该结构根据固定数量的输入元素计算中间状态。一维卷积核宽度为k,接受k个d维词向量,每个卷积核的权重 W \in \mathbb{R}^{2d*kd},偏置 b_{w} \in \mathbb{R}^{2d},卷积运算后得到一个 Y=[A\; B] \in \mathbb{R}^{2d} 维的向量,输入 GLU 再得到d维向量:v([A\; B])=A \otimes \sigma (B) \in \mathbb{R}^{d}A,B \in \mathbb{R}^{d}。论文中将卷积与 GLU 预算合并称为一个卷积 block,加入残差连接后,第 l 层 block 的输出为:

        h_{i}^{l}=v(W^{l}[h_{i-k/2}^{l-1},...,h_{i+k/2}^{l-1}]+b_{w}^{l})+h_{i}^{l-1}

一维卷积与二维卷积的区别:ConvS2S 中的一维卷积只在时间维度上平移,且由于语言不具备可伸缩性(如图像进行均匀的下采样后不改变图像特征),stride 固定为1;在图像处理中,一个卷积层通常含有多个 filter 以获取图像不同的 pattern,而 ConvS2S 中每层只有一个 filter。

Decoder 第 l 层计算出第 i 个词对应的输出 h_{i} 后,再获取第 i 个目标词 y_{i} 对应的 embedding g_{i},结合Encoder 各层卷积后得到的 z_{j}^{u} 计算 attention:

        d_{i}^{l}=W_{d}^{l}h_{i}^{l}+b_{d}^{l}+g_{i}

        a_{ij}=\frac{\exp (d_{i}^{l}\cdot z_{j}^{u})}{\sum _{t=1}^{m}\exp (d_{i}^{l}\cdot z_{t}^{u})}

        c_{i}^{l}=\sum _{j=1}^{m}a_{ij}^{l}(z_{j}^{u}+e_{j})

        更新  h_{i}^{l}:=h_{i}^{l}+c_{i}^{l}

对于 Encoder,通过填充每一层的输入来确保卷积层的输出与输入长度匹配。对于 Decoder,由于没有未来的信息可用,对输入的左侧和右侧均填充 k-1 个零向量(直角三角形),然后从卷积输出的末尾移除k个元素。通过对第i步顶层的输出 h_i^L 计算下一步的输出:

        p(y_{i+1}|y_{1},...,y_{i},\mathbf{x})=\mathrm{softmax}(W_{o}h_{i}^{L}+b_{o}) \in \mathbb{R}^{T}

https://blog.csdn.net/mudongcd0419/article/details/78353256

四、Transformer

论文《Attention is all you need》弃用了传统 Encoder-Decoder 模型必须结合CNN或者RNN的模式,提出了只用attention机制的 Transformer 模型。实验表明该模型不仅可以获得较高的质量及并行性,而且训练时间显著缩短。实验表明该模型不仅可以获得较高的质量及并行性,而且训练时间显著缩短。相比之下RNN难以处理较长的序列,顺序依赖导致不能并行训练;而CNN 虽然可以并行训练,但是单卷积层无法捕获远距离特征,需要使用膨胀卷积、加深CNN层数 & skip connection 、Norm等优化技术。

Transformer 由具有 self-attention 和 point-wise 全连接层的堆叠Encoder和Decoder组成,模型输入类似CNN 需要设定最大长度,padding句子后得到定长输入。Self-attention 会让当前输入与句子中任意词关联后embedding,从而解决了长距离依赖问题,但并未将位置信息编码。所以不像RNN或CNN,Transformer 必须明确的在输入端将位置信息进行编码 Positional Encoding。

1、Encoder

由N = 6个同样的层堆叠组成,每层有两个子层:multi-head attention和前馈神经网络position-wise fc FFN,子层均引入残差连接和层归一化(Add&Norm)。每个子层的输出是 LayerNorm(x+Sublayer(x)),为了实现所有残差连接,令所有子层和embedding层的输出维度都为 d_{model}=512

2、Decoder

结构类似Encoder,增加了一个Masked Multi-head Attention,以防止某位置对其后续位置的attention,这种masking确保了位置i的预测只能依赖于位置i以前的已知输出。

令输入图像的shape为[N, C, H, W],归一化的方法有(https://blog.csdn.net/liuxiao214/article/details/81037416):

(1)BatchNorm 2015:在batch上,对NHW做归一化,训练前要充分 shuffle,适用于batchsize较大、数据分布比较接近的情况;不适用于动态的网络结构和RNN。

(2)LayerNorm 2016:在一层所有神经元输入的方向上,对CHW归一化,同层输入拥有相同的均值和方差,不同的输入样本有不同的均值和方差(BN中则对不同神经元输入计算均值和方差,同一个batch中的输入拥有相同的均值和方差);LN不依赖于batch的大小和输入序列的深度,因此可以用于batchsize为1和RNN中对变长输入序列的normalize操作。

(3)InstanceNorm 2017:在像素上对HW做归一化,用于图像风格化迁移。

(4)GroupNorm 2018:将channel分group,然后在group内做归一化。

(5)SwitchableNorm 2018:通过学习BN、LN、IN的重要性权重从而在它们之间进行切换,让网络自己学习应该使用什么归一化方法。

3、Attention

Attention函数将 query 和一组 key-value pairs 映射为output,output 为 values 的加权和,分配给每个 value 的权重由 query 与相应 key 的相关性函数计算。

(1)Scaled Dot-Product

输入包括维度为 d_{k} 的Queries和Keys以及维度为 d_{v} 的Values,可将输入序列的每个词看作为一系列成对的 <位置Key, 元素Value>(即<PositionEncode, WordEmbed--之前的h>),每个目标词是Query--之前的s,通过点积计算Query和各个Key的相似性,得到每个Key对应Value的权重系数attention weight,权重系数代表信息的重要性,决定应该将注意力放在Values的哪部分。乘以 1/\sqrt{d_{k}} 可以避免点积过大时,softmax后的梯度消失问题。再对Values进行加权求和,得到最终的Attention / context vector。

        \mathrm{Attention}(Q,K,V)=\mathrm{softmax}(\frac{QK^{\mathrm{T}}}{\sqrt{d_{k}}})V

(2)Multi-Head Attention

论文对 Queries, Keys, Values 进行 h 次不同的线性映射,分别attention后concat(类似CNN)。每次Q,K,V进行线性变换的参数W是不一样的。然后将 h 次的放缩点积attention结果进行拼接,再进行一次线性变换得到的值作为多头attention的结果。通过多头注意力,模型能够 jointly attend 不同子空间的不同位置信息。

        \mathrm{MutiHead}(Q, K, V)=\mathrm{Concat}(\mathrm{head_{1}},...,\mathrm{head_{h}})W^{O}

                \mathrm{where} \; \mathrm{head_{i}}=\mathrm{Attention}(QW_{i}^{Q},KW_{i}^{K},VW_{i}^{V})

其中映射参数矩阵 W_{i}^{Q}, W_{i}^{K}\in \mathbb{R}^{d_{\mathrm{model}}\times d_{k}},W_{i}^{V} \in \mathbb{R}^{d_{\mathrm{model}}\times d_{v}},W^{O} \in \mathbb{R}^{hd_{v} \times d_{\mathrm{model}}},论文中取 h=8,\; d_{k}=d_{v}=d_{\mathrm{model}}/h=64,由于每个head的维度减小,总的计算花销与全维的单头注意相近。

 

Transformer使用了3种不同的多头注意力

a. Encoder-Decoder:Q来自Decoder的前一层,K和V来自Encoder的输出。这使得Decoder中的每一个位置都可以处理输入序列中的所有位置。

b. Encoder:Q, K, V均来自Encoder前一层的输出。

c. Decoder:每个位置可以attend当前及前面的所有位置,为了保持解码器的auto-regressive特性,需要在attention中加入mask(设为−∞)以防止当前词的生成依赖于未来的词(屏蔽左向信息流)。

 

(3)Position-wise Feed-Forward Networks

这个全连接的前馈神经该网络包括两个参数不同的线性变换,中间为ReLU激活(即 使用大小为1的卷积核进行两次卷积)。输入和输出的维度为 d_{model}=512,内层的维度为 d_{ff}=2048

        \mathrm{FFN}(x)=\max(0,\: xW_{1}+b_{1})W_{2}+b_{2}

(4)Position Encoding

由于模型不包含RNN和CNN,不能捕捉序列的顺序信息。如果将K,V按行打乱顺序(相当于打乱句子中的词序),那么attention的结果还是一样的。这就表明了目前为止,Attention模型只是一个非常精妙的“词袋模型”。为了使模型能够利用序列的顺序,必须注入关于序列的相对位置或绝对位置的信息,即结合每个词的 positional encoding 与 input embedding,位置编码与词嵌入编码具有相同的维度 d_{model},可以对二者进行求和。获得位置编码的方法有:根据任务训练向量、或构造固定公式直接获得。

        \mathrm{PE}_{(pos, 2i)}=\sin (pos/10000^{2i/d_{\mathrm{model}}})

        \mathrm{PE}_{(pos, 2i+1)}=\cos (pos/10000^{2i/d_{\mathrm{model}}})

论文使用不同频率的正弦和余弦函数,pos 为位置信息,i 为 dimension。位置编码的每个维度都对应一个正弦曲线,波长从2π(i=0)到10000*2π(2i=d_{model})。这个函数可以让模型很容易地通过相对位置来学习attend,对于offset k,\mathrm{PE_{pos+k}} 可以表示为 \mathrm{PE_{pos}} 的一个线性函数(\sin(\alpha +\beta )=\sin \alpha \cos\beta +\cos\alpha \sin \beta\cos(\alpha +\beta )=\cos\alpha \cos\beta - \sin\alpha \sin \beta)。

 

Auto recursive decoding

图中Encoding、Decoding均有三层,Decoder第一个预测结果 <start> 符号, 是完全通过Encoding里的attention vector 做出的决策;第二个及以后的预测结果,都是基于Encoding attention vector 和已经翻译过的所有单词的 attention vector 做出的决策。随着翻译出来的句子越来越长,翻译下一个单词的运算量也就会相应增加,复杂度是 (n^2d),其中n是翻译句子的长度,d是词向量的维度。

参考资料

https://blog.csdn.net/sinat_26917383/article/details/75050225

https://www.jianshu.com/p/3f2d4bc126e6

https://blog.csdn.net/weixin_42446330/article/details/86710838

https://zhuanlan.zhihu.com/c_188941548

https://kexue.fm/archives/4765

https://www.imooc.com/article/51468

http://nlp.seas.harvard.edu/2018/04/03/attention.html

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
传统NLP方法主要使用规则和统计学习方法来处理自然语言文本,例如基于n-gram的语言模型、基于HMM的分词和词性标注算法等。这些方法需要手动设计特征和规则,并且通常难以处理长文本和复杂语法。 End-to-End Seq2Seq模型是一种基于神经网络的序列到序列模型,可以将一个序列映射为另一个序列。它广泛用于机器翻译、文本摘要、对话系统等任务。它通常由两个循环神经网络(RNN)组成,一个编码器将输入序列编码为固定长度的向量,另一个解码器将此向量解码为输出序列。 Encoder-Decoder模型也是一种基于神经网络的序列到序列模型,它与Seq2Seq模型类似,但它通常使用卷积神经网络(CNN)或递归神经网络(RNN)作为编码器和解码器。它常用于机器翻译、语音识别等任务。 Transformer模型是一种基于自注意力机制的神经网络模型,它可以在不使用RNN和CNN的情况下捕捉序列中的长期依赖关系。它广泛用于机器翻译、文本摘要、问答系统等任务,例如Google的翻译系统就是基于Transformer模型实现的。 BERT模型(Bidirectional Encoder Representations from Transformers)是一种基于Transformer模型的预训练语言模型,它可以在大规模无标注文本上进行预训练,然后在少量标注数据上进行微调,用于各种自然语言处理任务,例如文本分类、命名实体识别、机器翻译等。 GPT模型(Generative Pre-training Transformer)也是一种基于Transformer模型的预训练语言模型,它可以在大规模无标注文本上进行预训练,然后在特定的任务上进行微调,例如文本生成、对话系统等。与BERT不同的是,GPT是一个单向的语言模型,只能生成单向的文本。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值