Tensorflow基础知识

1. tf.expand_dims()

作用:给函数增加纬度。
参数:

tf.expand_dims(
input,   # 输入张量
axis=None,  #  给定张量输入input,此操作为选择维度索引值,在输入形状的维度索引值的轴处插入1的维度。 维度索引值的轴从零开始; 如果您指定轴是负数,则从最后纬度处加1个纬度。
name=None,
dim=None
)

举例

eg:
t = tf.constant([1,2])
t.shape
    TensorShape([2])
t.numpy()
    array([1, 2], dtype=int32)

t1 = tf.expand_dims(t,0) #
t1.shape
     TensorShape([1, 2])
t1.numpy()
    array([[1, 2]], dtype=int32)

t2 = tf.expand_dims(t,1)
t2.shape
    TensorShape([2, 1])
t2.numpy()
    array([[1],
           [2]], dtype=int32)

t3 = tf.expand_dims(t,-1)  # 默认最后一个纬度加上
t3.shape
    TensorShape([2, 1])
t3.numpy()
    array([[1],
           [2]], dtype=int32)

2 . tf.nn.embedding_lookup

(1)embedding原理:Rm到Rn的一种线性映射。 x–>Mx. 当x是一个标准的基向量的时候,Mx对应矩阵M中的一列,即为对应id的向量表示。
(2)tf.nn.embedding_lookup()根据input_ids的id,寻找embeddings中第id行。 比如input_ids=[1,3,5], 则找出embedding中第1,3,5行,组成一个tensor。

#!/usr/bin/env/python
# coding=utf-8
import tensorflow as tf
import numpy as np

# 定义一个未知变量input_ids用于存储索引
input_ids = tf.placeholder(dtype=tf.int32, shape=[None])

# 定义一个已知变量embedding,是一个5*5的对角矩阵
# embedding = tf.Variable(np.identity(5, dtype=np.int32))

# 或者随机一个矩阵
embedding =  np.asarray([[0.1, 0.2, 0.3], [1.1, 1.2, 1.3], [2.1, 2.2, 2.3], [3.1, 3.2, 3.3], [4.1, 4.2, 4.3]])

# 根据input_ids中的id,查找embedding中对应的元素
input_embedding = tf.nn.embedding_lookup(embedding, input_ids)   # 感觉有些embedding hash的做法, 即初始化一个向量矩阵,将inputsid映射到这个矩阵上

sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer()) #  变量初始化
# print(embedding.eval())  # 查看embedding的数据
print(sess.run(input_embedding, feed_dict={input_ids: [1, 2, 3, 0, 3, 2, 1]}))


首先id,你可以理解成某个特征的编码值,比如兔子的年龄最大5岁,一只3岁的兔子的编码值 [0 0 1 0 0],之后用这个编码值embedding lookup一个 5 * N(5是兔子年龄的范围,N是要生成的兔子年龄特征的维度),则得出3岁这个特征的embedding。 即:特征 -》 编码 -》 emb的流程。 训练流程:需要是个完整的神经网络,需要定义loss,定义了loss,反向传播的时候就会利用梯度更新embedding lookup查找的那个矩阵(你可以理解这个矩阵也是神经网络的参数,和普通的神经网络的 w和b值没啥区别就好
embedding_lookup()的原理并不是相乘,而是根据input_id直接找到embedding的第几行第几行返回,所以就比你input_ids和embedding矩阵相乘要快的多
函数:
tf.nn.embedding_lookup(
               params,
               ids,
               partition_strategy='mod',
               name=None,
              validate_indices=True,
              max_norm=None
)

参数说明:
params: 表示完整的嵌入张量,或者除了第一维度之外具有相同形状的P个张量的列表,表示经分割的嵌入张量
ids: 一个类型为int32或int64的Tensor,包含要在params中查找的id
partition_strategy: 指定分区策略的字符串,如果len(params)> 1,则相关。当前支持“div”和“mod”。 默认为“mod”
name: 操作名称(可选)
validate_indices: 是否验证收集索引
max_norm: 如果不是None,嵌入值将被l2归一化为max_norm的值

tf.nn.embedding_lookup()函数的用法主要是选取一个张量里面索引对应的元素
tf.nn.embedding_lookup(tensor,id):即tensor就是输入的张量,id 就是张量对应的索引
tf.nn.embedding_lookup()就是根据input_ids中的id,寻找embeddings中的第id行。比如input_ids=[1,3,5],则找出embeddings中第1,3,5行,组成一个tensor返回
embedding_lookup不是简单的查表,id对应的向量是可以训练的,训练参数个数应该是 category num*embedding size,也就是说lookup是一种全连接层

3. tf.get_variable的使用方法

该函数共有十一个参数,常用的有:名称name、变量规格shape、变量类型dtype、变量初始化方式initializer、所属于的集合collections。
def get_variable(name,
shape=None,
dtype=None,
initializer=None,
regularizer=None,
trainable=True,
collections=None,
caching_device=None,
partitioner=None,
validate_shape=True,
use_resource=None,
custom_getter=None):

该函数的作用是创建新的tensorflow变量,常见的initializer有:
常量初始化器tf.constant_initializer、
正太分布初始化器tf.random_normal_initializer、
截断正态分布初始化器tf.truncated_normal_initializer、
均匀分布初始化器tf.random_uniform_initializer。

例子:
 import tensorflow as tf;
 import numpy as np;

 #常量初始化器
 v1_cons = tf.get_variable('v1_cons', shape=[1,4], initializer=tf.constant_initializer())  # 常量初始化
 v2_cons = tf.get_variable('v2_cons', shape=[1,4], initializer=tf.constant_initializer(9))
 #正太分布初始化器
 v1_nor = tf.get_variable('v1_nor', shape=[1,4], initializer=tf.random_normal_initializer())  # 正太分布初始化
 v2_nor = tf.get_variable('v2_nor', shape=[1,4], initializer=tf.random_normal_initializer(mean=0, stddev=5, seed=0))#均值、方差、种子值
 #截断正态分布初始化器
 v1_trun = tf.get_variable('v1_trun', shape=[1,4], initializer=tf.truncated_normal_initializer())
 v2_trun = tf.get_variable('v2_trun', shape=[1,4], initializer=tf.truncated_normal_initializer(mean=0, stddev=5, seed=0))#均值、方差、种子值
 #均匀分布初始化器
 v1_uni = tf.get_variable('v1_uni', shape=[1,4], initializer=tf.random_uniform_initializer())
 v2_uni = tf.get_variable('v2_uni', shape=[1,4], initializer=tf.random_uniform_initializer(maxval=-1., minval=1., seed=0))#最大值、最小值、种子值


 with tf.Session() as sess:
     sess.run(tf.global_variables_initializer())
     print("常量初始化器v1_cons:",sess.run(v1_cons))
     print("常量初始化器v2_cons:",sess.run(v2_cons))
     print("正太分布初始化器v1_nor:",sess.run(v1_nor))
     print("正太分布初始化器v2_nor:",sess.run(v2_nor))
     print("截断正态分布初始化器v1_trun:",sess.run(v1_trun))
     print("截断正态分布初始化器v2_trun:",sess.run(v2_trun))
     print("均匀分布初始化器v1_uni:",sess.run(v1_uni))
     print("均匀分布初始化器v2_uni:",sess.run(v2_uni))


https://www.w3cschool.cn/tensorflow_python/tensorflow_python-2kdb2my1.html     ---tensorflow文档

https://ot.icourse163.org/#/book?courseId=100

#===================

4. tf.tile()

tensorflow中的tile()函数是用来对张量(Tensor)进行扩展的,其特点是对当前张量内的数据进行一定规则的复制。最终的输出张量维度不变。
tf.tile(
input, # 输入的张量
multiples, # input按纬度进行扩展,如果input是二维,multiples=[2,3],表示input 按行复制2遍,按列复制3变
name=None
)

例子:
import tensorflow as tf

a = tf.constant([[1, 2], [3, 4], [5, 6]], dtype=tf.float32)
a1 = tf.tile(a, [2, 3])
with tf.Session() as sess:
    print(sess.run(a))
    print(sess.run(tf.shape(a)))
    print(sess.run(a1))
    print(sess.run(tf.shape(a1)))
=======
[[1. 2.]
 [3. 4.]
 [5. 6.]]
[3 2]
[[1. 2. 1. 2. 1. 2.]
 [3. 4. 3. 4. 3. 4.]
 [5. 6. 5. 6. 5. 6.]
 [1. 2. 1. 2. 1. 2.]
 [3. 4. 3. 4. 3. 4.]
 [5. 6. 5. 6. 5. 6.]]
[6 6]

#========================

5. tf.slice的应用

tf.slice(input_, # 被切的张量
begin, # 每一个纬度开始切割的位置
size, # 每一个纬度切割的个数
name=None)

 例子:
 t = tf.constant([[[0, 0, 0], [1, 1, 1]], [[2, 2, 2], [3, 3, 3]], [[4, 4, 4], [5, 5, 5]]])
 print(t.eval())
[[[0 0 0]
  [1 1 1]]

 [[2 2 2]
  [3 3 3]]

 [[4 4 4]
  [5 5 5]]]

t.shape = TensorShape([Dimension(3), Dimension(2), Dimension(3)])  #[3,2,3]

 a = tf.slice(t, [1, 0, 0], [1, 2, 3])
 # begin = [1, 0, 0]
 # size = [1,2,3]
 以上表示: 
 该数据集的shape=[3,2,3],总共三个维度。
 第一纬度从第二个记录开始(对应begin=1),且切割1个记录(对应size=1)。
 第二个纬度从第一个记录开始切割(对应begin=0),且切割2个记录(对应size=2)。
 第三个纬度,从第一个记录开始切割(对应bgin=0),且切割3个记录(对应size=3)。 
 注意: bigin, size中对应维度值不能大于shape[3,2,3]
 注意: 第三纬度的size=3. 但是在第二纬度已切取的所有数据集上。
 python index 从0开始。

 a = tf.slice(t, [1, 0, 0], [1, 2, 3])

 j : 第几个batch
 NEG: 负样本个数, 2
 i:第几个负样本
t = tf.constant([[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]])
NEG = 2
i = 0
a= tf.slice(t, [ NEG + i, 0], [1, -1])
 tf.slice(doc_neg_rnn_output, [ NEG + i, 0], [1, -1])  # [1,-1], 1表示第一纬切一个,-1表示第二个纬度全部。

#============================

6 tf.reduce_sum()

什么是reduce
这个词字面上来讲,大多称作“归约”,但这个词太专业了,以至于第一眼看不出来意思。我更倾向于解释为“塌缩”,这样就形象多了。
对一个n维的情况进行reduce,就是将执行操作的这个维度“塌缩”。还是上面tf.reduce_sum(a, axis=1)的例子,输出[[ 4, 6], [12, 14]]是二维,显然是被“塌缩”了,
塌缩的哪个维度呢?就是被操作的维度,第2个维度,也就是axis=1(0开始索引)。tf.reduce_sum(a, axis=1)具体执行步骤如下:找到a中axis=1的元素,
也就是[1,2],[3,4],[5,6],[7,8]这4个数组(两两一组,因为前两个和后两个的地位相同)在axis=1的维度进行相加也就是[1,2]+[3,4]=[4,6],[5,6]+[7,8]=[12, 14]
“塌缩”这一维度,也就是说“掉一层方括号”,得出[[ 4, 6], [12, 14]]接下来是一个附加问题:
什么是keepdims上面的reduce已经解释了,“塌缩”的是被操作的维度,
那么keepdims也就是保持维度,直观来看就是“不掉一层方括号”,不掉哪层方括号呢?就是本来应该被塌缩的那一层(详细解释见评论)。
tf.reduce_sum(a, axis=1, keepdims=True)得出[[[ 4, 6]], [[12, 14]]],可以看到还是3维。这种尤其适合reduce完了要和别的同维元素相加的情况。

#===========================

7.tf.summary.merge_all()

可以将所有summary全部保存到磁盘,以便tensorboard显示。
#===============================

8. tf.random_uniform

tf.random_uniform(
shape,
minval=0, # 随机下限
maxval=None, # 随机上限
dtype=tf.float32, #值类型
seed=None, #随机种子
name=None # 名称
)

9. StringLookup

将字符串转为对应位置的索引值。

import tensorflow as tf
from tensorflow.keras.layers.experimental.preprocessing import StringLookup
vocabulary = ['a','b','c','d']
lookup = StringLookup(
    vocabulary=vocabulary,
    mask_token=None,
    oov_token="[UNK]",
    num_oov_indices=1
)
lookup.get_vocabulary()
user_ids = tf.constant([['a','b', 'c', 'd', 'a'],['a','e', 'c', 'c', 'a']])
a = lookup(user_ids)
<tf.Tensor: shape=(2, 5), dtype=int64, numpy=
array([[1, 2, 3, 4, 1],
       [1, 0, 3, 3, 1]])>

10. Kera.layers.Embedding:

作用: 将一些离散的特征值,通过向量表示。 例如,深度模型中将用户id作为特征输入,如果是one-hot表示,数据特别系数。此时,可通过embedding形式表示id。


import tensorflow as tf
from tensorflow.keras.layers.experimental.preprocessing import StringLookup
vocabulary = ['a','b','c','d']
lookup = StringLookup(
    vocabulary=vocabulary,
    mask_token=None,
    oov_token="[UNK]",
    num_oov_indices=1
)
lookup.get_vocabulary()
user_ids = tf.constant(['a','b', 'c', 'd', 'a'])

encoded_feature = lookup(user_ids) # 将指定的字符转feature转为整数索引类型。
embedding_dims = int(math.sqrt(len(vocabulary)))
# Create an embedding layer with the specified dimensions.
embedding = layers.Embedding(
    input_dim=len(vocabulary)+1, output_dim=embedding_dims
)  # embedding层,需要提前准备好 输出纬度, 输出纬度。
# Convert the index values to embedding representations.
encoded_feature = embedding(encoded_feature)

<tf.Tensor: shape=(5, 2), dtype=float32, numpy=
array([[-0.00978594,  0.00189241],
       [-0.02078824, -0.03739477],
       [ 0.01578726,  0.02239802],
       [-0.02780767, -0.02573093],
       [-0.00978594,  0.00189241]], dtype=float32)>

11. csv文件读入datasets


LABEL_COLUMN = "label"
LABELS = [0, 1]
batch_size = 2

def show_batch(dataset):
  for batch, label in dataset.take(1):
    for key, value in batch.items():
      print("{:20s}: {}".format(key,value.numpy()))
def test_data():
    csv_file_path = input_file + 'test.csv'
    df = pd.DataFrame([[1, 2, 0],
                       [2, 3, 1],
                       [4, 5, 0]
                       ], columns=['age', 'index', 'label'])
    df.to_csv(input_file + 'test.csv', index=False)
    dataset = tf.data.experimental.make_csv_dataset(csv_file_path,
                                                    batch_size=batch_size,
                                                    label_name=LABEL_COLUMN,
                                                    na_value="?",
                                                    num_epochs=1,
                                                    ignore_errors=True)

    show_batch(dataset)

12 keras中sequential创建网络模型和functional api创建网络模型的区别

转载:https://www.cnblogs.com/lavender-pansy/p/13910855.html

  1. sequential 序列创建网络模型
    (1)sequential 创建网络模型,只能顺序创建。 网络是线性传播,不能共享输入和共享输出。
    (2)sequential 网络模型有两种方式
    使用add()方法将所需网络一层一层累加进去。
    直接在sequential方法中传入列表格式数据。(需要创建网络架构)
from keras.models import Sequential
from keras.layers import Dense
第一种方法

model = Sequential()
model.add(Dense(2, input_shape=(1,)))
model.add(Dense(1)

第二种方法
model = Sequential([
   Dense(2,input_shape = (1,))
   Dense(1)
])
  1. .使用functional api创建网络
    functional api创建网络时允许网络中有分支路线与汇合结点(使用concatenate方法)
    mnist_input = keras.layers.Input(shape=(28*28,1),name = 'input')
    lstm1 = keras.layers.LSTM(128,name = 'lstm1')(mnist_input)

    interp21 = keras.layers.Dense(64,activation='relu',name='interp21')(lstm1)
    interp22 = keras.layers.Dense(32,activation='relu',name='interp22')(interp21)
    interp23 = keras.layers.Dense(16,activation='relu',name='interp23')(interp22)

    interp11 = keras.layers.Dense(10,activation='relu',name='interp11')(lstm1)

    merge = keras.layers.concatenate([interp23,interp11],name='merge')

    output = keras.layers.Dense(10,activation='softmax',name='output')(merge)

    model = keras.models.Model(inputs=mnist_input,outputs=output)

    print(model.summary())
    return model

13. keras.preprocessing.sequence.pad_sequences()使用


keras.preprocessing.sequence.pad_sequences(sequences, 
	maxlen=None,
	dtype='int32',
	padding='pre',
	truncating='pre', 
	value=0.)
	
sequences:浮点数或整数构成的两层嵌套列表
maxlen:None或整数,为序列的最大长度。大于此长度的序列将被截短,小于此长度的序列将在后部填0.
dtype:返回的numpy array的数据类型
padding:‘pre’或‘post’,确定当需要补0时,在序列的起始还是结尾补`
truncating:‘pre’或‘post’,确定当需要截断序列时,从起始还是结尾截断
value:浮点数,此值将在填充时代替默认的填充值0
举例:
(1) 对 数值list进行padding:
ls =  [23, 34]
>>> pad_sequences([ls], padding='post', maxlen = MAX_ITEM_NUM)
array([[23, 34,      0,      0,      0,      0,      0,      0,
             0,      0]], dtype=int32)

#注意: course_index_dict 是 itemid 与index 的映射。
#注意这里x需要加括号。这里输入数据必须是list。如果 pandding是id序列,这里输入必须是二纬list。例如: [[23, 34], [34, 23]]2)对文本进行pading:

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
sentences = [
    '中 国 你 好'
]

tokenizer = Tokenizer(num_words=100)
tokenizer.fit_on_texts(sentences)
word_index = tokenizer.word_index
print(word_index)
sequences = tokenizer.texts_to_sequences(sentences)
padded = pad_sequences(sentences, maxlen=5)
print("\nWord Index = " , word_index)
print("\nSequences = " , sequences)
print("\nPadded Sequences:")
print(padded)
# 如果输入的是文本, 输入是一纬list, 通过" "间隔。 如:[ '中 国 你 好']
    

14. tf.multiply 与.tf.matmul()区别

1.tf.multiply()两个矩阵中对应元素各自相乘

格式: tf.multiply(x, y, name=None)
参数:
x: 一个类型为:half, float32, float64, uint8, int8, uint16, int16, int32, int64, complex64, complex128的张量。
y: 一个类型跟张量x相同的张量。
返回值: x * y element-wise.
注意:
(1)multiply这个函数实现的是元素级别的相乘,也就是两个相乘的数元素各自相乘,而不是矩阵乘法,注意和tf.matmul区别。
(2)两个相乘的数必须有相同的数据类型,不然就会报错。

2.tf.matmul()将矩阵a乘以矩阵b,生成a * b。 矩阵乘法。
注意:当a,b 进行transpose时,只对内部的2层进行转置。 2层之外的不进行变化 。

格式: tf.matmul(a, b, transpose_a=False, transpose_b=False, adjoint_a=False, adjoint_b=False, a_is_sparse=False, b_is_sparse=False, name=None)
The inputs must, following any transpositions, be tensors of rank >= 2
where the inner 2 dimensions specify valid matrix multiplication dimensions,
and any further outer dimensions specify matching batch size.
参数:
a: 一个类型为 float16, float32, float64, int32, complex64, complex128 且张量秩 > 1 的张量。
b: 一个类型跟张量a相同的张量。
transpose_a: 如果为真, a则在进行乘法计算前进行转置。
transpose_b: 如果为真, b则在进行乘法计算前进行转置。
adjoint_a: 如果为真, a则在进行乘法计算前进行共轭和转置。
adjoint_b: 如果为真, b则在进行乘法计算前进行共轭和转置。
a_is_sparse: 如果为真, a会被处理为稀疏矩阵。
b_is_sparse: 如果为真, b会被处理为稀疏矩阵。
name: 操作的名字(可选参数)
返回值: 一个跟张量a和张量b类型一样的张量且最内部矩阵是a和b中的相应矩阵的乘积。
注意:
(1)输入必须是矩阵(或者是张量秩 >2的张量,表示成批的矩阵),并且其在转置之后有相匹配的矩阵尺寸。
(2)两个矩阵必须都是同样的类型,支持的类型如下:float16, float32, float64, int32, complex64, complex128。
引发错误:
ValueError: 如果transpose_a 和 adjoint_a, 或 transpose_b 和 adjoint_b 都被设置为真

在这里插入图片描述

15. tf.transpose() 应用

tf.transpose(a,
perm=None, # 设定哪些纬度进行转置。
conjugate=False,
name=“transpose”)

key_list = tf.convert_to_tensor(np.asarray(
        [[[1., 1., 2., 4.],
          [4., 1., 1., 3.],
          [1., 1., 2., 1.]],
         [[1., 0., 2., 1.],
          [1., 2., 1., 2.],
          [1., 0., 2., 1.]]]
)) # TensorShape([2, 3, 4])

# perm 默认没有设置,则所有维度进行转置
a = tf.transpose(key_list)  #<tf.Tensor: shape=(4, 3, 2)
默认transpose
<tf.Tensor: shape=(4, 3, 2), dtype=float64, numpy=
array([[[1., 1.],
        [4., 1.],
        [1., 1.]],
       [[1., 0.],
        [1., 2.],
        [1., 0.]],
       [[2., 2.],
        [1., 1.],
        [2., 2.]],
       [[4., 1.],
        [3., 2.],
        [1., 1.]]])>

# 有的时候,第一纬度是batch_size不需要转置。则perm=[0,2,1]
a = tf.transpose(key_list, perm=[0,2,1]) 
<tf.Tensor: shape=(2, 4, 3), dtype=float64, numpy=
array([[[1., 4., 1.],
        [1., 1., 1.],
        [2., 1., 2.],
        [4., 3., 1.]],

       [[1., 1., 1.],
        [0., 2., 0.],
        [2., 1., 2.],
        [1., 2., 1.]]])>


16. tf.nn.softmax() 应用

参数说明:
logits, 需归一化的值
axis=None, 在哪个维度上进行归一化。默认是最后一个纬度
name=None

举例:
softmax = tf.exp(logits) / tf.reduce_sum(tf.exp(logits), axis)

(1)调用方法
softmax = tf.nn.softmax([-1.0, 0., 1.])
  <tf.Tensor: shape=(3,), dtype=float32,
  numpy=array([0.09003057, 0.24472848, 0.66524094], dtype=float32)>2)自定义计算计算  
z = tf.constant(-1.0)
<tf.Tensor: shape=(), dtype=float32, numpy=0.36787945>
y = tf.constant(0.)
<tf.Tensor: shape=(), dtype=float32, numpy=1.0>
x = tf.constant(1.0)
<tf.Tensor: shape=(), dtype=float32, numpy=2.7182817>
softmax = tf.exp(tf.constant(-1.0)) / (0.36787945 + 1.0 + 2.7182817)
<tf.Tensor: shape=(), dtype=float32, numpy=0.09003058>

两种方法计算基本一致。

17. tf.reshape() 应用

函数说明:
tf.reshape(
tensor, shape, name=None
)

shape: 待转换的tensor

# shape的某个纬度制定为-1时,
# 1)如果整个shape只有一个维度且是-1, 则默认是展开为1维
# 2)如果整个shape是多个维度, 则先按非-1的纬度进行展开, 剩下的默认是-1的纬度。
t = [[1, 2, 3],
     [4, 5, 6]]
tf.reshape(t, [-1])
"""<tf.Tensor: shape=(6,), dtype=int32, numpy=array([1, 2, 3, 4, 5, 6], dtype=int32)>
"""
tf.reshape(t, [3, -1])
"""
<tf.Tensor: shape=(3, 2), dtype=int32, numpy=
array([[1, 2],
       [3, 4],
       [5, 6]], dtype=int32)>
"""
tf.reshape(t, [-1, 2])
"""
 tf.reshape(t, [-1, 2])
<tf.Tensor: shape=(3, 2), dtype=int32, numpy=
array([[1, 2],
       [3, 4],
       [5, 6]], dtype=int32)>
"""

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值