在深度学习的知识宝库中,卷积神经网络(CNN)广泛应用于视觉,视频等二维或者多维的图像领域。卷积网络具有深度,可并行等多种优良特性,那么这种技术是否可以应用于解单维度的时间序列问题呢?本文介绍一种最近提出的新技术:时间卷积神经网络 (Temporal Convolutional Network,TCN),由Lea等人于2016年首次提出,起初应用于视频里动作的分割,后逐渐拓展到了一般性时序领域。在学习这篇文章之前,需要您有足够的关于神经网络和卷积神经网络的知识储备,如果你对这些不熟悉,你可以先行学习我之前的文章:神经网络1-基础过关-CSDN博客,神经网络2-卷积神经网络一文深度读懂-CSDN博客。
到目前为止,深度学习背景下的序列建模主题主要与递归神经网络架构(如LSTM和GRU)有关。然而,随着深度学习的高速发展,这种思维方式已经过时。在对序列数据进行建模时,最近很多学者将卷积网络作为主要候选者之一加以考虑。他们能够表明,在许多任务中,卷积网络可以取得比RNNs更好的性能,同时避免了递归模型的常见缺陷,如梯度爆炸/消失问题或缺乏内存保留。此外,使用卷积网络而不是递归网络可以提高性能,因为它允许并行计算输出。
1. 马尔可夫模型
时间序列预测,最容易想到的就是马尔可夫模型:
就是计算某一个时刻的输出值,已知条件就是这个时刻之前的所有特征值。上面公式中,表示概率,表示时刻的输出值(标签),表示时刻的特征值。
如果我们使用长短时记忆网络LSTM或者是门控循环单元GRU这样的循环神经网络RNN模型,自然是可以处理这样的时间序列模型的,毕竟RNN生来就是为了这个的。但是这个时间序列模型,宏观上思考的话,其实就是对这个时刻之前的数据做某个操作,然后生成一个标签,回想一下在卷积在图像中的操作,其实有异曲同工之妙。
2.一维卷积
相似于卷积神经网络,一维卷积网络(通常用于处理时间序列数据或序列数据)以一个三维张量作为输入,也输出一个三维张量。输入张量具有形状(batch_size、input_length、input_size),而输出张量具有形状(batch_size、input_length、output_size)。这些维度分别表示:
- batch_size:批大小(Batch Size)。
batch_size
表示一次前向传播(forward pass)中通过网络的样本数量。在训练过程中,模型会一次处理一个小批量(mini-batch)的数据,而不是整个数据集。这样做可以加速训练过程,因为GPU和其他并行计算硬件可以更有效地处理小批量数据。 - input_length:输入长度(Input Length)。
input_length
表示每个输入样本的时间步长或序列长度。例如,在处理时间序列数据时,它可能代表时间序列的长度。在文本处理任务中,这可能是一个句子的长度(以单词或字符为单位)。 - input_size:输入尺寸(Input Size)。
input_size
表示每个时间步长或序列位置上的特征数量。在文本处理中,这通常是嵌入层(embedding layer)的维度,即每个单词或字符被转换成的向量大小。在时间序列分析中,它可能代表每个时间点的测量值数量。
例如,如果你正在处理一个文本分类任务,其中每个样本是一个由100个单词组成的句子,每个单词被表示为一个50维的嵌入向量,并且你一次处理32个句子,那么你的输入张量将具有形状 (32, 100, 50)
,其中 batch_size
是 32,input_length
是 100,input_size
是 50。
由于每一层都有相同的输入和输出长度,所以只有输入和输出张量的第三维是不同的。在单变量情况下,input_size和output_size都等于1。在更一般的多变量情况下,input_size和output_size可能不同。一维卷积网络通过在这些输入上应用一维卷积核来学习局部特征,这些卷积核在输入数据的长度上滑动,从而提取出有用的信息。这些网络特别适合处理具有时间依赖性的数据,因为它们能够学习输入数据中的长期和短期依赖关系。
为了了解单个层如何将其输入转换为输出,让我们看一下针对批处理中的一个元素的处理情况(对批处理中的每个元素都进行相同的处理)。让我们从最简单的例子开始,其中input_channels和output_channels都等于1。在这种情况下,我们看到的是一维输入和输出张量。下图显示了输出张量的一个元素是如何计算的。
我们可以看到,元素输出的一个特征表示也是一个长度为output_size的序列,他是通过滑动窗口与长度为kernel_size的连续元素序列做点积运算获得的。通过设置卷积核kernel的个数,我们可以控制输出元素的output_channels,这点和卷积神经网络是一致的。在上面的例子中,我们选择了一个长度为3的kernel_size。为了得到输出,我们在输入序列上同样使用长度为3的时间窗口来取值,将和卷积核长度相同的输入序列和已代表学习权值的核向量做点积,输出下一个阶段的元素。对于所有的输入元素在同一层卷积层都采用相同的kernal,同时在输入序列上使用的滑动窗口步长也是自行设置,对于本预测模型,stride 总是设置为1。下图显示了两个连续的时间窗口的输出元素及其各自的输入子序列。
为了更直观的演示,我们来看下面这个例子。假设有一个时间序列,总共有五个时间点,比方说股市,有一个股票的价格波动:[10,13,12,14,15]:
我们使用的卷积核大小为2,那么可想而知,对上面5个数据做一个卷积核为2的卷积是什么样子的:
五个数据经过一次卷积,可以变成四个数据,但是每一个卷积后的数据都是基于两个原始数据得到的,所以说,目前卷积的感受野是2。可以看到是输入是5个数据,但是经过卷积,变成4个数据了,在图像中有一个概念是通过填充(padding)来保证卷积前后特征图尺寸不变,所以在时间序列中,依然使用padding来保证尺寸不变:
尽管padding可以是左右两头都增加0(零填充),但如果这样选择,实际的输出数据会产生6个新数据。但是秉着:“输入输出尺寸相同”和“我们不能知道未来的数据”,所以最后边那个未来的padding,就省略掉了。如果padding是1的话,就是上图的效果。
3. 因果卷积
传统的卷积神经网络(CNN)在处理图像数据时表现出色,但对于序列数据,如时间序列或文本数据,传统的CNN可能并不适用。这是因为传统的CNN在处理图像时可以自由地访问任何位置的数据,而在处理序列数据时,我们只能从左到右(或从上到下)依次访问数据。因果卷积正是为了解决这个问题而提出的。
因果卷积(Causal Convolution)是一种特殊的卷积操作,它是在wavenet这个网络中提出的,之后被用在了TCN中,在处理时间序列数据或序列数据时非常有用。因果卷积的主要特点是,对于上一层t时刻的值,它只依赖于下一层t时刻及其之前的值。这意味着,在因果卷积中,未来的数据不会影响当前时刻的输出,它只考虑时间上的因果关系。因果卷积通过使用零填充(zero-padding)来确保卷积核在滑动时不会看到未来的数据。这样,每个输出值都只依赖于当前时刻及之前的输入值,从而保持了时间上的因果关系。
例如,对于对于一个时间序列,对于其中任意一个时刻,其输出序列的第个元素可能只依赖于索引为的输入序列中的元素。换句话说,输出序列中的元素只能依赖于与其位置相同的输入序列中的元素以及在它之前的元素。
这种特性使得因果卷积非常适合用于处理需要严格遵循时间顺序的任务,如语音识别、文本生成等。此外,因果卷积还可以有效地减少模型中的参数数量,从而降低模型的复杂度。这是因为,由于因果卷积的限制,模型不需要考虑未来的数据,因此可以减小卷积核的大小,从而减少参数数量。我们依然通过示例来说明因果卷积。
如前所述,为了确保一个输出张量与输入张量具有相同的长度,我们需要进行零填充。如果我们只在输入张量的左侧填充零,那么就可以保证因果卷积。要理解这一点,请考虑最右边的输出元素。假设输入序列的右边没有填充,它所依赖的最后一个元素就是输入的最后一个元素。现在考虑输出序列中倒数第二个输出元素。与最后一个输出元素相比,倒数第二个输出元素的内核窗口是向左移动了1,这意味着它在输入序列中最右边的依赖项也变成了输入序列中倒数第二个元素。根据归纳,对于输出序列中的每个元素,其在输入序列中的最新依赖项与其本身具有相同的索引。下图展示了一个input_length为4,kernel_size为3的示例。
我们可以看到,在两个条目的左填充为零的情况下,我们可以获得相同的输出长度,同时遵守因果关系规则。事实上,在不扩展输出特征的情况下,维持输入长度所需的零填充条目的数量总是等于。
我们将上面的概念应用到之前的股票的预测的案例中,希望这个决策模型可以考虑到这个时间点之前的4个时间点的股票价格进行决策,总共有3种决策:0:不操作,1:买入,2:卖出。
这其实就是一个分类问题。因为要求视野域(感受野)是4,最后输出一个分类结果。所以按照上面的设想,我们可以堆积3个卷积核为2的1维卷积层:三个卷积核分别应用于三个层的卷积操作,最后只输出一个结果,而这个结果就是我们需要的分类结果。三次卷积,可以让最后的输出,拥有4个视野域。就像是上图中红色的部分,就是做出一个决策的过程。
股票数据,往往是按照分钟记录的,那少说也是十万、百万的数据量,我们决策,想要考虑之前1000个时间点呢?视野域要是1000,那意味着要999层卷积?啥计算机吃得消这样的计算。所以需要引入膨胀因果卷积。
4. 膨胀因果卷积
因果卷积则确保在处理序列数据时,模型的输出仅依赖于当前和过去的信息,不依赖未来的信息。这种特性使得因果卷积在时间序列分析和音频生成模型中广泛应用。膨胀卷积通过在卷积核的相邻元素之间插入“空洞”(即膨胀),使得卷积核能够覆盖更大的输入区域,从而增加模型的感受野,而不增加额外的参数。这种特性使得膨胀卷积在图像处理和音频处理中非常有用,尤其是在需要捕捉更广泛上下文信息的场合。
膨胀因果卷积(Dilated Causal Convolution)是结合了膨胀卷积(Dilated Convolution)和因果卷积(Causal Convolution)的一种卷积核,它结合了这两种卷积的优点,既增加了模型的感受野,又保持了时间因果性。这种结合允许模型同时捕获长距离的序列依赖关系和保持时间的因果性,特别适用于需要模型理解长距离依赖的时间序列数据,例如在自然语言处理(NLP)和语音识别中。
一个典型的应用案例是Google的WaveNet模型,它使用了一系列膨胀因果卷积层来生成音频样本,每个卷积层的膨胀率成倍增加,这使得网络能够有效地捕捉音频数据中的长期依赖关系。通过其独特的设计,膨胀因果卷积在处理序列数据时,提供了对长距离依赖的理解,同时保持时间序列的因果性,使其在语音合成、音乐生成和某些类型的时间序列预测任务中非常有效。
膨胀因果卷积简单来说,就是卷积层覆盖的输入序列的上下文范围,膨胀是指输入序列的元素之间的距离,该范围内的元素用于计算输出序列的一个条目。因此,传统的卷积层可以看作是扩展度为1的扩散层,即用于结算输出条目的输入序列彼此之间是相邻的。下图显示了一个扩展度为2的扩散层的示例,表示用于计算下一个输出条目的输入元素,彼此之间间隔了一个元素。其中,input_length为4,kernel_size为3。
与扩散度为1的情况相比,扩散度为2的卷积核一次性覆盖的输入序列的元素范围是5而不是3,这种卷积核一次卷积计算覆盖的元素范围称为该层的接收场。卷积层的接收场(Receptive Field,简称RF)指的是在神经元连接到前一层的输出体积上的局部区域,包括其深度。接收场是3D张量,其深度等于上一层中的体积的深度。接收场的大小决定了神经元能够“看到”的输入数据的范围,即神经元对输入数据的敏感程度。因此在上图中,该层的接收场是沿5而不是3的长度扩展。
当引入膨胀卷积(也称为空洞卷积)时,接收场的大小会增加,而不需要增加内核的大小或网络的深度。膨胀长度(dilation rate)定义了卷积核中元素之间的间隔数量。当膨胀长度为2时,这意味着在每个相邻的卷积核元素之间有2个单位的间隔(对应到扩展度就变成了扩展度为3)。因此,尽管卷积核的物理大小仍然是3,但由于这些间隔,它实际上会覆盖一个更大的输入区域。
例如,对于内核大小为3且膨胀长度为2的膨胀卷积,其接收场的大小可以计算如下:
接收场大小 = (内核大小 - 1) * 膨胀长度 + 内核大小
将给定的值代入上述公式:
接收场大小 = (3 - 1) * 2 + 3
= 2 * 2 + 3
= 4 + 3
= 7
因此,具有内核大小为3和膨胀长度为2的膨胀卷积,其实际沿长度扩展的接收场大小为7。这意味着该卷积操作能够“看到”输入数据上连续7个单位的长度,尽管其卷积核的物理大小仅为3。
更普遍地,具有内核大小的膨胀长度的接收场沿的长度扩展。如果是固定的,那么仍然需要输入张量的长度为线性的数字才能实现完全的接收场覆盖。这个问题可以通过在层中向上移动时的值呈指数增加来解决。为此,我们选择一个常数,它将使我们根据其层数来计算特定层的膨胀长度,即。下图显示了一个网络,其中input_length为10,kernel_size为3,dilation_base为2,这将导致3个膨胀的卷积层完全覆盖。
注意:
- dilation_base定义了卷积核中元素之间的间隔数量,即膨胀率(dilation rate)。dilation_base的具体取值会影响卷积核在输入数据上的采样方式,dilation_base的值决定了卷积核在滑动过程中跳过多少个输入位置。例如,当dilation_base为1时,膨胀卷积退化为标准的卷积操作;当dilation_base大于1时,卷积核中的元素之间会跳过dilation_base-1个输入位置,从而实现膨胀效果。
- 在膨胀卷积中,dilation_base的引入允许卷积核在不增加其物理大小的情况下覆盖更大的输入区域,从而增加模型的感受野(Receptive Field)。这有助于模型捕捉输入数据中的长距离依赖关系,同时保持计算效率和参数数量的优势。需要注意的是,dilation_base的取值应该根据具体任务和数据特性进行合理选择,以确保模型能够有效地学习到输入数据中的关键信息。同时,过高的dilation_base值可能会导致模型难以学习到局部特征,因此需要在实际应用中进行权衡和调整。
这里我们只显示影响输出最后一个值的输入的影响。同样,只显示最后一个输出值所必需的补零项。显然,最后的输出值依赖于整个输入覆盖率。实际上,给定超参数,input_length最多可以使用15,同时保持完全的接收野覆盖。一般来说,每增加一层,当前接受野宽度就增加一个值,其中计算为, 表示新层下面的层数。因此,给出了膨胀基时TCN的感受场宽度、核大小和层数之间的关系为:
然而,根据和的值,这个接受野可能会有“洞”。考虑以下网络,其dilation_base为3,内核大小为2:
接受野的范围确实大于输入的大小(即15)。然而,接受野是有洞的;也就是说,在输入序列中有输出值不依赖的条目(如上面红色所示)。为了解决这个问题,我们需要将内核大小增加到3,或者将膨胀基数减小到2。一般来说,对于没有孔的感受野,核的大小至少要与膨胀基一样大。
考虑到这些观察结果,我们可以计算出我们的网络需要多少层才能覆盖整个历史。给定核大小,膨胀基,其中,输入长度,为了实现全历史覆盖,必须满足以下不等式:
我们可以求解,得到所需的最小层数:
我们可以看到,在输入长度方面,层数现在是对数的,而不是线性的。这是一个显著的改进,可以在不牺牲接受野覆盖率的情况下实现。现在,唯一需要指定的是每一层所需的零填充项的数量。假设膨胀基为,核大小为,当前层以下有个层,则当前层所需的补零项数计算如下:
还是接着上面的例子,当扩展度为2的时候,与之前的区别有两个:
- 看红色区域:可以看到卷积核大小依然是2,但是卷积核之间变得空洞了,隔过去了一个数据;如果dilation=3的话,那么可以想而知,这个卷积核中间会空的更大,会隔过去两个数据。
- 看淡绿色数据:因为dilation变大了,所以相应的padding的数量从1变成了2,所以为了保证输入输出的特征维度相同,padding的数值等于dalition的数值(在卷积核是2的情况下,严格说:padding=(kernel_size-1)*dilation)
然后我们依然实现上面那个例子,每次决策想要视野域为4:
可以看到,第一次卷积使用dilation=1的卷积,然后第二次使用dilation=2的卷积,这样通过两次卷积就可以实现视野域是4。那么假设视野域要是8呢?那就再加一个dilation=4的卷积。dilation的值是2的次方,然后视野域也是2的次方的增长,那么就算是要1000视野域,那十层大概就行了。
5. TCN的结构
TCN基本就是一个膨胀因果卷积的过程,只是上面我们实现因果卷积就只有一个卷积层。而TCN的稍微复杂一点:
-
卷积结束后会因为padding导致卷积之后的新数据的尺寸B>输入数据的尺寸A,所以只保留输出数据中前面A个数据;
-
卷积之后加上个ReLU和Dropout层。
-
然后TCN中并不是每一次卷积都会扩大一倍的dilation,而是每两次扩大一倍的dilation。
-
总之TCN中的基本组件:TemporalBlock()是两个dilation相同的卷积层,整体结构是:卷积+修改数据尺寸+relu+dropout+卷积+修改数据尺寸+relu+dropout。
TCN可以接受任意长度的序列,并将其输出为相同长度。因果卷积在使用一维全卷积网络结构时使用。一个关键的特征是时刻的输出只与之前的元素进行卷积。下图展示了一个基于TCN的“编码器-解码器”结构:
神经网络的编码器-解码器(Encoder-Decoder)是一种深度学习模型架构,广泛应用于各种任务,如图像生成、自然语言处理、语音识别等。
- 编码器(Encoder)通常由多层神经网络组成,负责将输入数据映射到一个低维表示。这个低维表示可以被视为对原始数据的一种抽象,具有更好的可解释性和更强的表达能力。例如,在图像生成任务中,编码器将输入图像转换为一个向量或矩阵,该向量或矩阵包含了输入图像的主要特征信息。
- 解码器(Decoder)与编码器相反,它将低维表示映射回高维空间以生成输出。在许多情况下,解码器采用与编码器相同的架构,并使用反向传播算法来更新权重和偏置。例如,在图像生成任务中,解码器从编码后的低维向量或矩阵开始生成一张新图片。
编码器-解码器架构的一个典型应用是自动编码器(Autoencoder),它由一个编码器和一个解码器组成。自动编码器通过学习如何重建输入数据来压缩信息并发现潜在特征。训练过程中,在给定输入数据后,自动编码器首先使用其编码器将数据压缩为一个低维向量,然后使用解码器将该向量解码回原始数据。模型的目标是最小化重构误差,即输入和输出之间的差异。
关于编码器-解码器的深度分析请参见下一篇文章。