干货|如何使用最流行框架Tensorflow进行时间序列分析?

如何使用最流行框架Tensorflow进行时间序列分析?(一)

TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。它灵活的架构让你可以在多种平台上展开计算,例如台式计算机中的一个或多个CPU(或GPU),服务器,移动设备等等。TensorFlow 最初由Google大脑小组(隶属于Google机器智能研究机构)的研究员和工程师们开发出来,用于机器学习和深度神经网络方面的研究,但这个系统的通用性使其也可广泛用于其他计算领域。

时间序列分析在计量经济学和财务分析中具有重要意义,但也可以应用于了解趋势做决策和对行为模式的变化做出反应的领域。其中例如,作为主要石油和天然气供应商的MapR融合数据平台客户将传感器放在井上,将数据发送到MapR Streams,然后将其用于趋势监测井的状况,如体积和温度。在金融方面,时间序列分析用于股票价格,资产和商品的价格的预测。计量经济学家长期利用“差分自回归移动平均模型”(ARIMA)模型进行单变量预测。

ARIMA模型已经使用了几十年,并且很好理解。然而,随着机器学习的兴起,以及最近的深度学习,其他模式正在被探索和利用。

深度学习(DL)是基于一组算法的机器学习的分支,它通过使用由多个非线性变换组成的人造神经网络(ANN)架构来尝试对数据进行高级抽象然后建模。更为流行的DL神经网络之一是循环神经网络(RNN)。RNN是依赖于其输入的顺序性质的一类神经网络。这样的输入可以是文本,语音,时间序列,以及序列中的元素的出现取决于在它之前出现的元素。例如,一句话中的下一个字,如果有人写“杂货”最有可能是“商店”而不是“学校”。在这种情况下,给定这个序列,RNN可能预测是商店而不是学校。

人工神经网络

实际上,事实证明,虽然神经网络有时是令人畏惧的结构,但使它们工作的机制出奇地简单:随机梯度下降。对于我们网络中的每个参数(如权重或偏差),我们所要做的就是计算相对于损耗的参数的导数,并在相反方向微调一点。

ANNs使用称为反向传播(有想了解BP算法的可以参考BP算法双向传,链式求导最缠绵)的方法来调整和优化结果。反向传播是一个两步过程,其中输入通过正向传播馈送到神经网络中,并且在通过激活函数变换之前与(最初随机的)权重和偏差相乘。你的神经网络的深度将取决于你的输入应该经过多少变换。一旦正向传播完成,反向传播步骤通过计算产生误差的权重的偏导数来调整误差。一旦调整权重,模型将重复正向和反向传播步骤的过程,以最小化误差率直到收敛。下图中你看到这是一个只有一个隐藏层的ANN,所以反向传播不需要执行多个梯度下降计算。

640?wx_fmt=png

循环神经网络

循环神经网络(RNN)被称为循环是因为它们对输入序列中的所有元素执行相同的计算。由于RNN的广泛应用,RNN正在变得非常受欢迎。它们可以分析时间序列数据,如股票价格,并提供预测。在自动驾驶系统中,他们可以预测汽车轨迹并帮助避免事故。他们可以将句子,文档或音频样本作为输入,它们也可以应用于自然语言处理(NLP)系统,如自动翻译,语音对文本或情感分析。

640?wx_fmt=png

上图是RNN架构的示例,并且我们看到xt是时间步长t的输入。例如,x1可能是时间段1中的股票的第一个价格。st是在时间步长tn处的隐藏状态,并且使用激活函数基于先前的隐藏状态和当前步骤的输入来计算。St-1通常被初始化为零。ot是步骤t的输出。例如,如果我们想预测序列中的下一个值,那么它将是我们时间序列中概率的向量。

RNN隐藏层的成长是依赖于先前输入的隐藏状态或记忆,捕获到目前为止所看到的内容。任何时间点的隐藏状态的值都是前一时间步骤中的隐藏状态值和当前时间的输入值进行函数计算的结果。RNN具有与ANN不同的结构,并且通过时间(BPTT)使用反向传播来计算每次迭代之后的梯度下降。

一个小例子:

此示例使用3个节点的小型MapR群集完成。此示例将使用以下内容:

  • Python 3.5

  • TensorFlow 1.0.1

  • Red Hat 6.9

如果你使用Anaconda,你需要保证你能够安装TensorFlow 1.0.1版本在你本地的机器上。此代码将不能在TensorFlow <1.0版本上使用。如果TensorFlow版本相同,则可以在本地机器上运行并传输到集群。其他需要考虑的深度学习库是MXNet,Caffe2,Torch和Theano。Keras是另一个为TensorFlow或Theano提供python包的深度学习库。

640?wx_fmt=png

MapR提供了用户喜好的集成Jupyter Notebook(或Zeppelin)的功能。我们将在这里显示的是数据管道的尾端。在分布式环境中运行RNN时间序列模型的真正价值是你可以构建的数据流水线,将聚合的系列数据推送到可以馈送到TensorFlow计算图中的格式。

如果我正在聚合来自多个设备(IDS,syslogs等)的网络流,并且我想预测未来的网络流量模式行为,我可以使用MapR Streams建立一个实时数据管道,将这些数据聚合成一个队列,进入我的TensorFlow模型。对于这个例子,我在集群上只使用一个节点,但是我可以在其他两个节点上安装TensorFlow,并且可以有三个TF模型运行不同的超参数。对于这个例子,我生成了一些虚拟数据。

640?wx_fmt=png

640?wx_fmt=png

我们在我们的数据中有209个观察结果。我want确保我对每个批次输入都有相同的观察次数。

我们看到的是我们的训练数据集由10个批次组成,包含20个观测值。每个观察值是单个值的序列。

640?wx_fmt=png

现在我们有了我们的数据,我们来创建一个将执行计算的TensorFlow图。

640?wx_fmt=png

这里有很多事情需要处理。例如我们正在指定我们用来预测的周期数。我们指定我们的变量占位符。我们初始化一种使用的RNN单元格(大小100)和我们想要的激活函数的类型。ReLU代表“整流线性单元”,是默认的激活功能,但如果需要,可以更改为Sigmoid,Hyberbolic Tangent(Tanh)等。

我们希望我们的输出与我们的输入格式相同,我们可以使用损失函数来比较我们的结果。在这种情况下,我们使用均方误差(MSE),因为这是一个回归问题,我们的目标是最小化实际和预测之间的差异。如果我们处理分类结果,我们可能会使用交叉熵。现在我们定义了这个损失函数,可以定义TensorFlow中的训练操作,这将优化我们的输入和输出网络。要执行优化,我们将使用Adam优化器。Adam优化器是一个很好的通用优化器,可以通过反向传播实现渐变下降。现在是时候在我们的训练数据上实施这个模型了。

640?wx_fmt=png

我们将指定我们的批次训练序列循环的迭代/纪元的数量。接着,我们创建我们的图形对象(tf.Session()),并初始化我们的数据,以便在我们遍历历元时被馈送到模型中。缩写输出显示每100个纪元后的MSE。随着我们的模型提供数据向前和反向传播运行,它调整应用于输入的权重并运行另一个训练时期,我们的MSE得到了持续改善(减少)。最后,一旦模型完成,它将接受参数并将其应用于测试数据中,以Y的预测输出。

我们来看看我们的预测跟实际相差多少。对于我们的测试数据,我们集中在整个209个周期的最后20个时期。

640?wx_fmt=png

640?wx_fmt=png

看来这还有一些改进的空间。这可以通过改变隐藏的神经元的数量或增加迭代的数量来完成。优化我们的模式是一个试错的过程,但我们有一个好的开始。这是随机数据,所以我们期待着很好的结果,但是也许将这个模型应用到实时系列中会给ARIMA模型带来一些竞争压力。

数据科学家因为RNN(和深度学习)的出现,有了更多可用的选项以此来解决更多有趣的问题。许多数据科学家面临的一个问题是,一旦我们进行了优化,我们如何自动化我们的分析运行?拥有像MapR这样的平台允许这种能力,因为你可以在大型数据环境中构建,训练,测试和优化你的模型。在这个例子中,我们只使用了10个训练批次。如果我的数据允许我利用数百批次,而不仅仅是20个时期,我想我一定能改进这种模式。一旦我做到了,我可以把它打包成一个自动化脚本,在一个单独的节点,一个GPU节点,一个Docker容器中运行。这就是在融合数据平台上进行数据科学和深度学习的力量。

如何使用最流行框架Tensorflow进行时间序列分析?(二)

时间序列分析是一种动态数据处理的统计方法。根据对系统进行观测得到的时间序列数据,用曲线拟合的方法对系统进行客观的描述。

如今,时间序列数据出现在金融,信号处理,语音识别和医学等诸多领域。解决时间序列问题的标准方法通常需要手动提炼数据特征,然后才能将其输入到机器学习算法中。这通常还要求开发设计人员掌握数据所属学科领域的知识特征,以便在算法中加入特征过滤。例如,如果处理信号(即EEG信号的分类),则需要掌握的知识特征涉及各种频带的功率谱及Hjorth参数。对于认真钻研本领域的程序猿来说,这简直就是噩梦。

那么是不是不掌握这些学科领域的知识特征,我们就没有办法进行模型设计了呢?

其实答案不然,在图像分类领域也出现了类似的情况。但是,随着深度学习的出现,卷积神经网络(CNN)的性能已经可以胜过这种人工提取特征的方法。CNN不需要任何手动设置任何的图像特征。在训练过程中,随着层次越来越深,CNN越来越复杂,进而它自己会学习得到许多过滤器,并在最终的分类器中使用它们。

在这篇博客文章中,我将讨论使用深度学习的方法对时间序列数据进行分类,而无需手动设计特征。我在本文中将使用到的例子是UCI存储库中经典的人类活动识别(HAR)数据集。该数据集包含原始时间序列数据,以及具有561个预处理数据的工程特征。在博客中我会比较使用工程特征与深度学习这两种方法(卷积和复现神经网络),并表明深度学习可以超越前者的性能。

在本文中我将使用Tensorflow来实现和训练博客中所用到的模型。在下面的讨论中,提供了代码片段来解释实现过程。有关完整的代码,请参阅我的Github资源库

卷积神经网络(CNN)

第一步是将数据投射到具有一定形状的numpy数组中:(batch_size, seq_len, n_channels)其中batch_size是训练期间批次中的示例数,seq_len是时间序列的长度(在我们的情况下n_channels128),并且是进行测量的通道的数量。在本文的小例子中,有9个通道,每3个坐标轴包括3个不同的加速度测量。每次观察有6类活动LAYING, STANDING, SITTING, WALKING_DOWNSTAIRS, WALKING_UPSTAIRS, WALKING首先,我们为输入到计算图的数据创建占位符:

graph = tf.Graph()with graph.as_default():
inputs_ = tf.placeholder(tf.float32, [None, seq_len, n_channels],name = 'inputs')labels_ = tf.placeholder(tf.float32, [None, n_classes], name = 'labels')keep_prob_ = tf.placeholder(tf.float32, name = 'keep')learning_rate_ = tf.placeholder(tf.float32, name = 'learning_rate')

inputs是将输入的张量馈送到计算图,并将其数组第一个位置设置为None,以便允许可变的批量大小。labels_是要预测的一个热编码的标签,keep_prob的作用是在退出正则化中保持概率来防止过度拟合,并且learning_rate_Adam优化器中使用的学习率。

我们将通过使用移动序列的一维内核构造卷积层(与使用2d卷积的图像不同)来构造卷积层,这些内核作为在训练过程中的过滤器。像许多CNN架构一样,层越深,过滤器数越多。每个卷积之后是汇集层,以此减少序列长度。下面是可能可以使用的CNN架构的简单图片:

640?wx_fmt=png

上面描述的卷积层如下实现:

with graph.as_default():# (batch, 128, 9) -&gt; (batch, 32, 18)conv1 = tf.layers.conv1d(inputs=inputs_, filters=18, kernel_size=2, strides=1,padding='same', activation = tf.nn.relu)max_pool_1 = tf.layers.max_pooling1d(inputs=conv1, pool_size=4, strides=4, padding='same')# (batch, 32, 18) -&gt; (batch, 8, 36)conv2 = tf.layers.conv1d(inputs=max_pool_1, filters=36, kernel_size=2, strides=1,padding='same', activation = tf.nn.relu)max_pool_2 = tf.layers.max_pooling1d(inputs=conv2, pool_size=4, strides=4, padding='same')# (batch, 8, 36) -&gt; (batch, 2, 72)conv3 = tf.layers.conv1d(inputs=max_pool_2, filters=72, kernel_size=2, strides=1,padding='same', activation = tf.nn.relu)max_pool_3 = tf.layers.max_pooling1d(inputs=conv3, pool_size=4, strides=4, padding='same')

一旦达到最后一层,我们需要张量平坦化并将其输送到具有正确数量的神经元的分类器中(上图中的144个)。模型功能:

1.    计算softmax交叉熵,这是多类问题中使用的标准损失度量。

2.    从最大概率以及精度预测类标签。

功能实现代码如下:

with graph.as_default():
    # Flatten and add dropout
    flat = tf.reshape(max_pool_3, (-1, 2*72))
    flat = tf.nn.dropout(flat, keep_prob=keep_prob_)
    # Predictions
    logits = tf.layers.dense(flat, n_classes)
# Cost function and optimizer
cost =        tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=labels_))
optimizer = tf.train.AdamOptimizer(learning_rate_).minimize(cost)
# Accuracy
correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(labels_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32), name='accuracy')

其余的实施部分涉及向图表馈送批次的训练数据并评估验证集的性能。最后,对测试集进行评估。采用上述架构batch_size=600learning_rate =0.001(默认值),keep_prob= 0.5500训练次数,我们得到98的测试精度。下面的图表显示了训练/验证精度如何通过训练次数演变:

640?wx_fmt=png

长短期记忆网络(LSTM)

LSTM在处理基于文本的数据方面非常受欢迎,在情感分析,语言翻译和文本生成方面也相当成功。今天我们就用LSTM来解决我们今天的问题。以下是可以在我们的问题中使用的示例架构:

640?wx_fmt=png

为了将数据传送到网络中,我们需要将数组分成128个,每个的形状我们定义为:(batch_size, n_channels)。然后,单层神经元将把这些输入转换成LSTM细胞,每一个都具有维度lstm_size。该参数的大小选择要大于通道数。这是一种类似于在文本应用程序中嵌入图层的方式。为了实现,占位符与上述相同。以下代码段实现了LSTM层:

with graph.as_default():    # Construct the LSTM inputs and LSTM cells
    lstm_in = tf.transpose(inputs_, [1,0,2]) # reshape into (seq_len, N, channels)
    lstm_in = tf.reshape(lstm_in, [-1, n_channels]) # Now (seq_len*N, n_channels)
    # To cells
    lstm_in = tf.layers.dense(lstm_in, lstm_size, activation=None)
    # Open up the tensor into a list of seq_len pieces
    lstm_in = tf.split(lstm_in, seq_len, 0)
    # Add LSTM layers
    lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size)
    drop = tf.contrib.rnn.DropoutWrapper(lstm, output_keep_prob=keep_prob_)
    cell = tf.contrib.rnn.MultiRNNCell([drop] * lstm_layers)initial_state = cell.zero_state(batch_size, tf.float32)

上面的代码段中有一个重要的技术细节。我将阵列重新整形(batch_size, seq_len, n_channels)(seq_len, batch_size, n_channels),这样tf.split可以在每个步骤中将数据(由第零个索引)正确地分割成数组列表。其余的是LSTM实现的标准,包括构建层(包括正则化的退出),然后是定义初始状态。


下一步是通过网络实现前向传递和成本函数。一个重要的技术方面利用梯度剪辑,因为它通过防止反向传播期间的爆炸梯度来改善训练。

with graph.as_default():
    outputs, final_state = tf.contrib.rnn.static_rnn(cell, lstm_in, dtype=tf.float32,initial_state = initial_state)# We only need the last output tensor to pass into a classifier
	logits = tf.layers.dense(outputs[-1], n_classes, name='logits')
	# Cost function and optimizer
	cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=labels_))
	# Grad clipping
	train_op = tf.train.AdamOptimizer(learning_rate_)
	gradients = train_op.compute_gradients(cost)
	capped_gradients = [(tf.clip_by_value(grad, -1., 1.), var) for grad,var in gradients]
	optimizer = train_op.apply_gradients(capped_gradients)
	# Accuracy
	correct_pred = tf.equal(tf.argmax(logits, 1), tf.argmax(labels_, 1))
 	accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32), name='accuracy')

请注意,仅使用LSTM输出顶部序列的最后一个成员,因为我们正在尝试每个序列预测一个数字。其余的类似CNN,我们只需要将数据提供给图表进行训练。lstm_size=27,lstm_layers=2,batch_size=600learning_rate=0.0005,和keep_prob=0.5,我获得95%的准确度的测试集。这比CNN的结果还差,但还是不错的。这些超参数的更好选择会改进的结果。

与工程特征进行比较

在此之前,我已经使用561个预先设计的特征测试了一些关于这个问题的机器学习方法。性能最好的模型之一是梯度提升树(gradient booster)(树形或线性),其结果是96的精确度(您可以从这本笔记本中了解更多信息)。CNN架构优于梯度提升树,但LSTM的性能相较于梯度提升树(gradient booster)就稍差一些。

总结:

在这篇博客文章中,我已经说明了如何使用CNNLSTM进行时间序列分类,并证明深层架构可以胜过预先设计的功能特征训练的模型。除了达到更好的准确性外,深度学习模式还培养了自己的功能。这是非常可取的,因为人们不需要具有来自数据来源的领域专长,能够训练准确的模型。

我们在这篇文章中使用的序列相当小(128步)。人们可能会想,如果步骤数量很多,那么今天我讨论的这些架构的可训练性是否还有?如果有?会发生什么。我认为一种可能的架构将涉及LSTMCNN的组合,其对于较大的序列(即> 1000,对于LSTM是有问题的)可以更好地工作。因为在这种情况下,具有汇集作用的几个卷积就可以有效地减少前几个层中的步数,并且得到的较短的序列可以被反馈送到LSTM层。这种结构的一个例子最近被用于从移动设备记录的心房颤动检测。如果你有兴趣了解这种长序列的方法可以去研究它。转自:全球人工智能


感谢您的阅读,祝您一天好心情!



长按二维码,关注我们

640?wx_fmt=jpeg

### 回答1: Spark Streaming 和 Flink 都是流处理框架,但在一些方面有所不同。 1. 数据处理模型 Spark Streaming 基于批处理模型,将流数据分成一批批进行处理。而 Flink 则是基于流处理模型,可以实时处理数据流。 2. 窗口处理 Spark Streaming 的窗口处理是基于时间的,即将一段时间内的数据作为一个窗口进行处理。而 Flink 的窗口处理可以基于时间和数据量,可以更加灵活地进行窗口处理。 3. 状态管理 Spark Streaming 的状态管理是基于 RDD 的,需要将状态存储在内存中。而 Flink 的状态管理是基于内存和磁盘的,可以更加灵活地管理状态。 4. 容错性 Flink 的容错性比 Spark Streaming 更加强大,可以在节点故障时快速恢复,而 Spark Streaming 则需要重新计算整个批次的数据。 总的来说,Flink 在流处理方面更加强大和灵活,而 Spark Streaming 则更适合批处理和数据仓库等场景。 ### 回答2: Spark Streaming 和 Flink 都是流处理框架,它们都支持低延迟的流处理和高吞吐量的批处理。但是,它们在处理数据流的方式和性能上有许多不同之处。下面是它们的详细比较: 1. 处理模型 Spark Streaming 采用离散化流处理模型(DPM),将长周期的数据流划分为离散化的小批量,每个批次的数据被存储在 RDD 中进行处理,因此 Spark Streaming 具有较好的容错性和可靠性。而 Flink 采用连续流处理模型(CPM),能够在其流处理过程中进行事件时间处理和状态管理,因此 Flink 更适合处理需要精确时间戳和状态管理的应用场景。 2. 数据延迟 Spark Streaming 在处理数据流时会有一定的延迟,主要是由于对数据进行缓存和离散化处理的原因。而 Flink 的数据延迟比 Spark Streaming 更低,因为 Flink 的数据处理和计算过程是实时进行的,不需要缓存和离散化处理。 3. 机器资源和负载均衡 Spark Streaming 采用了 Spark 的机器资源调度和负载均衡机制,它们之间具有相同的容错和资源管理特性。而 Flink 使用 Yarn 和 Mesos 等分布式计算框架进行机器资源调度和负载均衡,因此 Flink 在大规模集群上的性能表现更好。 4. 数据窗口处理 Spark Streaming 提供了滑动、翻转和窗口操作等灵活的数据窗口处理功能,可以使用户更好地控制数据处理的逻辑。而 Flink 也提供了滚动窗口和滑动窗口处理功能,但相对于 Spark Streaming 更加灵活,可以在事件时间和处理时间上进行窗口处理,并且支持增量聚合和全量聚合两种方式。 5. 集成生态系统 Spark Streaming 作为 Apache Spark 的一部分,可以充分利用 Spark 的分布式计算和批处理生态系统,并且支持许多不同类型的数据源,包括Kafka、Flume和HDFS等。而 Flink 提供了完整的流处理生态系统,包括流SQL查询、流机器学习和流图形处理等功能,能够灵活地适应不同的业务场景。 总之,Spark Streaming 和 Flink 都是出色的流处理框架,在不同的场景下都能够发挥出很好的性能。选择哪种框架取决于实际需求和业务场景。 ### 回答3: Spark Streaming和Flink都是流处理引擎,但它们的设计和实现方式有所不同。在下面的对比中,我们将比较这两种流处理引擎的主要特点和差异。 1. 处理模型 Spark Streaming采用离散流处理模型,即将数据按时间间隔分割成一批一批数据进行处理。这种方式可以使得Spark Streaming具有高吞吐量和低延迟,但也会导致数据处理的粒度比较粗,难以应对大量实时事件的高吞吐量。 相比之下,Flink采用连续流处理模型,即数据的处理是连续的、实时的。与Spark Streaming不同,Flink的流处理引擎能够应对各种不同的实时场景。Flink的实时流处理能力更强,因此在某些特定的场景下,它的性能可能比Spark Streaming更好。 2. 窗口计算 Spark Streaming内置了许多的窗口计算支持,如滑动窗口、滚动窗口,但支持的窗口计算的灵活性较低,只适合于一些简单的窗口计算。而Flink的窗口计算支持非常灵活,可以支持任意窗口大小或滑动跨度。 3. 数据库支持 在处理大数据时,存储和读取数据是非常重要的。Spark Streaming通常使用HDFS作为其数据存储底层的系统。而Flink支持许多不同的数据存储形式,包括HDFS,以及许多其他开源和商业的数据存储,如Kafka、Cassandra和Elasticsearch等。 4. 处理性能 Spark Streaming的性能比Flink慢一些,尤其是在特定的情况下,例如在处理高吞吐量的数据时,在某些情况下可能受制于分批处理的架构。Flink通过其流处理模型和不同的调度器和优化器来支持更高效的实时数据处理。 5. 生态系统 Spark有着庞大的生态系统,具有成熟的ML库、图处理库、SQL框架等等。而Flink的生态系统相对较小,但它正在不断地发展壮大。 6. 规模性 Spark Streaming适用于规模小且不太复杂的项目。而Flink可扩展性更好,适用于更大、更复杂的项目。Flink也可以处理无限制的数据流。 综上所述,Spark Streaming和Flink都是流处理引擎,它们有各自的优缺点。在选择使用哪一个流处理引擎时,需要根据实际业务场景和需求进行选择。如果你的业务场景较为复杂,需要处理海量数据并且需要比较灵活的窗口计算支持,那么Flink可能是更好的选择;如果你只需要简单的流处理和一些通用的窗口计算,Spark Streaming是更为简单的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值